Why We Need m_Count in StockItem

Now it's time to clear up a point we've glossed over so far. We have already seen that the member initialization expression m_Count(0), present in the constructors for the StockItem object, is there just to make sure we don't have an uninitialized variable in the StockItem object — even though we won't be using m_Count in a StockItem object. While this is true as far as it goes, it doesn't answer the question of why we need this variable at all if we're not using it in the StockItem class. The clue to the answer is that we are using that member variable in the object pointed to by m_Worker (i.e., m_Worker->m_Count). But why don't we just add the m_Count variable when we create the UndatedStockItem class rather than carry along extra baggage in the StockItem class?

The answer is that m_Worker is not an UndatedStockItem* but a StockItem*. Remember, the compiler doesn't know the actual type of the object being pointed to at compile time; all it knows is the declared type of the pointer, which in this case is StockItem*. It must therefore use that declared type to determine what operations are permissible through that pointer. Hence, if there's no m_Count variable in a StockItem, the compiler won't let us refer to that variable through a StockItem*.

Susan wasn't sure of what I was trying to say here:

Susan: What do you mean, if there were no m_Count variable in a StockItem, we wouldn't be able to access it through a StockItem pointer?

Steve: The only member variables and member functions that you can access through a pointer are ones that exist in the class that pointer is declared as pointing to, no matter what type the pointer may really be pointing to. This is a consequence of C++'s “static type checking”; if we were allowed to refer to a member variable or function that might theoretically not be present in an object at run time, the compiler would have no way of knowing whether what we were trying to do was legal. Therefore, if we want to access a member variable called m_Count through a StockItem*, there has to be a member variable called m_Count in the StockItem class, even if we don't need it until we get to a class derived from StockItem.

This also brings up another point that may or may not be obvious to you: the workings of the polymorphic StockItem object don't depend on the fact that the DatedStockItem class is derived from UndatedStockItem. So long as both the UndatedStockItem and DatedStockItem classes are derived directly or indirectly from StockItem, we can use a StockItem* to refer to an object of either of these classes, UndatedStockItem or DatedStockItem, which is all that we need to make the idiom work.

Executing Code before the Beginning of main

We're almost ready to review what we've covered in this chapter, but first there's one issue that I have deferred explaining until now: how we can arrange for code to be executed before the beginning of main, and why we would want to do that.

Let's consider what would happen if we were to define a variable of a user-defined type as a global object. We know that all global objects are initialized before the beginning of main, according to the C++ language specification. But we also know that all objects of user-defined types are created via constructors, either written by us or created automatically by the compiler. When we define such a variable as a global object, the appropriate constructor is called before the beginning of main. Therefore, if we want to make sure that a particular piece of code is executed before the beginning of main, all we have to do is put it in a constructor and define a global object that is created by that constructor.

Why would we want to do that? To make polymorphism more flexible without losing the advantages of our “safe polymorphism” approach.

With our current implementation of safe polymorphism, our base StockItem class has to know about all the derived types that we create, because the constructors that create those derived types are called from constructors in the StockItem class.

But what if we wanted to be able to add new user-defined types derived from StockItem without changing the implementation of StockItem in any way? We can do this by defining special constructors for each derived type, then creating global objects of those types that use those special constructors. The special constructors are called before main and register their classes with a central “polymorphic object class registry function” that maintains a list of available classes. Whenever another part of the program wants to create an object of a polymorphic object type, it can ask the registry to create that object, providing whatever parameters are needed to select the appropriate type and initialize the object. The registry does this by calling an object creation function in the selected class. This approach enables us to add or remove classes when we link the program without making changes to the rest of the program, which allows other programmers to extend our classes without needing our source code.

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

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