Reimplementing the Standard Member Functions for the New Version of StockItem

We have seen how a polymorphic object works once it is set up, so let's continue our examination of the StockItem class by looking at the “standard” member functions; that is, the ones that are necessary to make it a concrete data type. As you may remember, these are the default constructor, the copy constructor, the assignment operator (operator =), and the destructor.

It may occur to you to wonder why we have to rewrite all these functions; what's wrong with the ones we've already written for StockItem? The answer is that these functions create, copy, and destroy objects of a given class. Now that we have changed the way we want to use the StockItem class and the way it works, we have to rewrite these functions to do the right thing in the new situation.

Susan wanted to make sure she understood where we were going with this:

Susan: Okay, then this is the new StockItem, not the old one. This is your manager StockItem that tells the worker what to do; the DatedStockItem is the worker, right?

Steve: Yes, this is the new type of StockItem. By the way, besides a DatedStockItem, an UndatedStockItem can also be a worker.

Let's start with the default constructor for this new StockItem class, shown in Figure 10.29.

Figure 10.29. Safe polymorphism: The default constructor for the polymorphic StockItem class (from codeitemp.cpp)
StockItem::StockItem()
: m_Count(0), m_Worker(new UndatedStockItem)
{
  m_Worker->m_Count = 1;
}

The first member initialization expression in this function merely initializes m_Count to 0. We won't actually be using this variable in a StockItem object, but I don't like leaving variables uninitialized.

The second member initialization expression, however, looks a bit odd. Why are we creating an UndatedStockItem object here when we have no data to put in it? Because we need some worker object to perform the work of a default-constructed StockItem. For example, if the user asks for the contents of the StockItem to be displayed on the screen with labels (by calling FormattedDisplay), we want the default values displayed with the appropriate labels, which can only be done by an object that has a working FormattedDisplay function. The StockItem class itself doesn't have any data except for the StockItem* and the m_Count variable (which we'll get to later). Therefore, all of the functionality of a StockItem has to be handed off to the worker object, which in this case is the newly created UndatedStockItem.

A default-constructed StockItem might look like (Figure 10.30). Susan didn't think the need for a valid object as the worker object for a default-constructed manager object was intuitively obvious.

Susan: I don't get why you say that we can only call the FormattedDisplay function with an object that has a working version of that function. That doesn't mean anything to me.

Steve: Well, what would happen if we called FormattedDisplay via a pointer that didn't point to anything? It wouldn't work, that's for sure.

In particular, any attempt by the user to call a virtual function through a pointer to a nonexistent object will fail, because there won't be a valid vtable address in that missing object. The most likely result will be a crash when the code tries to use a vtable entry that contains random garbage.

Figure 10.30. Safe polymorphism: A default-constructed polymorphic StockItem object


I still haven't explained exactly what the member initialization expression, m_Worker(new UndatedStockItem), actually does. On the surface, it's pretty simple: it creates a new UndatedStockItem object via the default constructor of that class and uses the address of the resulting object to initialize m_Worker.

However, there are some tricks in the implementation of the constructor of a worker class, and now is the time to see how such a constructor actually works. Figure 10.31 shows the code for the default constructor for the UndatedStockItem class.

Figure 10.31. Safe polymorphism: The default constructor for the UndatedStockItem class (from codeitemp.cpp)
UndatedStockItem::UndatedStockItem()
: StockItem(1),
  m_InStock(0),
  m_Price(0),
  m_MinimumStock(0),
  m_MinimumReorder(0),
  m_Name(),
  m_Distributor(),
  m_UPC()
{
}

Most of the code in this constructor is standard; all we're doing is initializing the values of the member variables to reasonable default values. But there's something a bit unusual about that base class initializer, StockItem(1). Before we get into exactly what the argument 1 means, it's important to understand why we must specify a base class initializer here.

As we saw in Chapter 9 in the discussion of Figure 9.33 on page 635, we use a base class initializer when we want to specify which base class constructor will initialize the base class part of a derived class object. If we don't specify any particular base class initializer, the base class part will be initialized with the default constructor for that base class. In the example in Chapter 9, we needed to call a specific base class constructor to fill in the fields in the base class part of the DatedStockItem object, which should seem reasonable enough. But that can't be the reason we need to specify a base class constructor here, because the StockItem object doesn't have any data fields that need to be initialized in our UndatedStockItem constructor. So why can't we just let the compiler use the default constructor for the base class part of an UndatedStockItem?

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset