Why We Need Polymorphic Objects

You may be wondering what an “idiom” is in programming. Well, in English or any other natural language, an idiom is a phrase whose meaning can't be derived directly from the meanings of its individual words. An example would be “to make good time”, which actually means “to proceed rapidly”. Similarly, the manager/worker idiom used to implement polymorphic objects has effects that aren't at all obvious from a casual inspection of its components.

I should tell you that many, if not most, professional C++ programmers don't know about this method of making polymorphism safe and easy to use for the application programmer. Why, then, am I including it in a book for relatively inexperienced programmers?

Because I believe it is the best solution to the very serious problems caused by dynamic memory allocation when using polymorphism. For that reason, every serious C++ programmer should know this idiom and understand how to apply it to real-life problems.

At this point, Susan was ready to give this new idea a shot, as the following exchange indicates:

Susan: Okay, I feel I have followed you fairly well up to the point of the big thing you're going to do here with the polymorphic objects. I think that stuff is going to take some real thinking time. I hope it goes well.

Steve: I hope so too.

Assuming that I have impressed the importance of this technique on you, how does it work? The most elementary answer is that it involves creating a set of classes that work as a team to present the appearance of a simple object that has the desired polymorphic behavior. The user of the class (i.e., the application programmer) doesn't have to know about any of the details of this idiom; he or she merely defines an object of the single visible class and that object does what the user wants with the help of an object of another class. James Coplien calls these two kinds of classes envelope and letter, respectively, but I'm going to call them manager and worker. I think these names are easier to remember because the outside world sees only objects of the manager class, which take credit for everything done by the polymorphic object, even though most of the work is actually done by objects of the worker classes.

As usual, all of the intricacies of the implementation are the responsibility of the class designers (us). However, before we get into the details of how a polymorphic object works, let's see how it affects the way we use the StockItem class. Figure 10.21 shows how we will use the new StockItem class; note the lack of deletes and the fact that the variables in Figure 10.21 are StockItems rather than StockItem pointers.[6]

[6] If you were wondering why the file name is different in this program than it was in the program in Figure 10.18, it's because the program in Figure 10.21 is using the “real” version of the StockItem class rather than the simplified one used by the program in Figure 10.18. Therefore, it needs more input to fill in the extra member variables.

Figure 10.21. Safe polymorphism: Using operator >> and operator << with a polymorphic StockItem (codepolyioc.cpp)
#include <iostream>
#include <fstream>

#include "Vec.h"
#include "itemp.h"
using namespace std;

int main()
{
   StockItem x;
   StockItem y;

   ifstream ShopInfo("shop22.in");

   ShopInfo >> x;

   ShopInfo >> y;

   cout << "A StockItem: " << endl;
   cout << x;

   cout << endl;

   cout << "A DatedStockItem: " << endl;
   cout << y;

   return 0;
}

I strongly recommend that you print out the files that contain the interface and the implementation of the polymorphic object version of StockItem, as well as the test program, to refer to as you go through this section of the chapter. Those files are itemp.h (StockItem interface in Figure 10.22), itempi.h (UndatedStockItem and DatedStockItem interfaces in Figure 10.23), itemp.cpp (UndatedStockItem and DatedStockItem implementation in Figure 10.24), and polyioc.cpp (test program in Figure 10.21).

You must be happy to see that we've eliminated the visible pointers in the new version of the example program, but how does it work? Let's start by looking at Figure 10.22, which shows the interface for the manager class StockItem. As we've discussed, this is the class of the objects that are visible to the user of the polymorphic object.

Figure 10.22. Safe polymorphism: The polymorphic object version of the StockItem interface (codeitemp.h)
// itemp.h

class StockItem
{
friend std::ostream& operator << (std::ostream& os,
  const StockItem& Item);

friend std::istream& operator >> (std::istream& is, StockItem& Item);

public:
  StockItem();
  StockItem(const StockItem& Item);
  StockItem& operator = (const StockItem& Item);
virtual ~StockItem();

  StockItem(std::string Name, short InStock,
  short Price, short MinimumStock,
  short MinimumReorder, std::string Distributor, std::string UPC);

  StockItem(std::string Name, short InStock,
  short Price, short MinimumStock,
  short MinimumReorder, std::string Distributor, std::string UPC,
  std::string Expires);

virtual bool CheckUPC(std::string UPC);
virtual void DeductSaleFromInventory(short QuantitySold);
virtual short GetInventory();
virtual std::string GetName();

virtual void Reorder(std::ostream& os);
virtual void FormattedDisplay(std::ostream& os);
virtual std::ostream& Write(std::ostream& os);

protected:
  StockItem(int);

protected:
  StockItem* m_Worker;
  short m_Count;
};

Unlike the classes we've dealt with before, where the member functions deserved most of our attention, possibly the most interesting point about this new version of the StockItem class is its member variables, especially the variable named m_Worker. It's a pointer, which isn't all that strange; the question is, what type of pointer?

It's a pointer to a StockItem — that is, a pointer to the same type of object that we're defining! Assuming that is useful, is it even legal?

Using a Base class Pointer to Point to Derived class Objects

Yes, it is legal, because the compiler can figure out how to allocate storage for a pointer to any type whether or not it knows the full definition of that type. However, this doesn't answer the question of why we would want a pointer to a StockItem in our StockItem class in the first place. The answer is that, as we saw in the discussion of polymorphism earlier in this chapter, a pointer to a StockItem can actually point to an object of any class derived from StockItem via public inheritance. We're going to make use of this fact to implement the bulk of the functionality of our StockItem objects in the classes UndatedStockItem and DatedStockItem, which are derived from StockItem.

Susan didn't think this use of a pointer to refer to a worker object was very obvious. Here's the discussion we had about it.

Susan: Ugh. What is m_Worker? Where did it come from, and why is it suddenly so popular? Don't tell me it's a pointer. I want to know exactly what it does.

Steve: It points to the “worker” object that actually does the work for the StockItem, which is why it is called m_Worker.

In essence, we're renaming the old StockItem class to UndatedStockItem and creating a new StockItem manager class that will handle interaction with the application programmer. Objects of this new StockItem class will pass the actual class-specific operations to an object of the UndatedStockItem or DatedStockItem class as needed.

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

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