Implementing Safe Polymorphism

Now that we have an overview of the structure of the classes we're designing, Figure 10.23 shows the interfaces for the worker classes UndatedStockItem and DatedStockItem.

Figure 10.23. Safe polymorphism: The UndatedStockItem and DatedStockItem interfaces for the polymorphic version of StockItem (codeitempi.h)
class UndatedStockItem : public StockItem
{
public:
 UndatedStockItem();

 UndatedStockItem(std::string Name, short InStock,
  short Price, short MinimumStock, short ReorderQuantity,
  std::string Distributor, std::string UPC);

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:
 short m_InStock;
 short m_Price;
 short m_MinimumStock;
 short m_MinimumReorder;
 std::string m_Name;
 std::string m_Distributor;
 std::string m_UPC;
};

class DatedStockItem : public UndatedStockItem
{
public:

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

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

protected:
static std::string Today();

protected:
 std::string m_Expires;
};


And Figure 10.24 shows the implementation of these classes.

Figure 10.24. Safe polymorphism: The implementation of the UndatedStockItem and DatedStockItem classes (codeitemp.cpp)
#include <iostream>
#include <iomanip>
#include <sstream>
#include <string>
#include "itemp.h"
#include "itempi.h"
#include <dos.h>
using namespace std;

//friend functions of StockItem
ostream& operator << (ostream& os, const StockItem& Item)
{
 return Item.m_Worker->Write(os);
}

istream& operator >> (istream& is, StockItem& Item)
{
 string Expires;
 string Name;
 short InStock;
 short Price;
 short MinimumStock;
 short MinimumReorder;
 string Distributor;
 string UPC;

 getline(is,Expires);
 getline(is,Name);
 is >> InStock;
 is >> MinimumStock;
 is >> Price;
 is >> MinimumReorder;
 is.ignore();
 getline(is,Distributor);
 getline(is,UPC);

 if (Expires == "0")
    {
    Item = StockItem(Name, InStock, Price, MinimumStock,
       MinimumReorder, Distributor, UPC);
    }
 else
   {
   Item = StockItem(Name, InStock, Price, MinimumStock,
      MinimumReorder, Distributor, UPC, Expires);
   }

  return is;

}

// StockItem member functions

StockItem::StockItem()
: m_Count(0), m_Worker(new UndatedStockItem)
{
  m_Worker->m_Count = 1;
}

StockItem::StockItem(const StockItem& Item)
: m_Count(0), m_Worker(Item.m_Worker)
{
  m_Worker->m_Count ++;
}

StockItem& StockItem::operator = (const StockItem& Item)
{
 StockItem* temp = m_Worker;

 m_Worker = Item.m_Worker;
 m_Worker->m_Count ++;

 temp->m_Count --;
 if (temp->m_Count <= 0)
   delete temp;

 return *this;
}

StockItem::~StockItem()
{
 if (m_Worker == 0)
   return;

 m_Worker->m_Count --;
 if (m_Worker->m_Count <= 0)
   delete m_Worker;
}

StockItem::StockItem(string Name, short InStock, // Undated
 short Price, short MinimumStock, short MinimumReorder,
  string Distributor, string UPC)
: m_Count(0),
  m_Worker(new UndatedStockItem(Name, InStock, Price,
  MinimumStock, MinimumReorder, Distributor, UPC))
{
  m_Worker->m_Count = 1;
}

StockItem::StockItem(int)
: m_Worker(0)
{
}

StockItem::StockItem(string Name, short InStock, // Dated
  short Price, short MinimumStock, short MinimumReorder,
  string Distributor, string UPC, string Expires)
: m_Count(0),
  m_Worker(new DatedStockItem(Name, InStock, Price,
  MinimumStock, MinimumReorder, Distributor, UPC, Expires))
{
  m_Worker->m_Count = 1;
}

bool StockItem::CheckUPC(string UPC)
{
  return m_Worker->CheckUPC(UPC);
}

short StockItem::GetInventory()
{
  return m_Worker->GetInventory();
}

void StockItem::DeductSaleFromInventory(short QuantitySold)
{
  m_Worker->DeductSaleFromInventory(QuantitySold);
}

string StockItem::GetName()
{
  return m_Worker->GetName();
}

ostream& StockItem::Write(ostream& os)
{
  exit(1); // should never get here
  return os;
}

void StockItem::Reorder(ostream& os)
{
  m_Worker->Reorder(os);
}


void StockItem::FormattedDisplay(ostream& os)
{
  m_Worker->FormattedDisplay(os);
}


// UndatedStockItem member functions

UndatedStockItem::UndatedStockItem()
: StockItem(1),
  m_InStock(0),
  m_Price(0),
  m_MinimumStock(0),
  m_MinimumReorder(0),
  m_Name(),
  m_Distributor(),
  m_UPC()
{
}

UndatedStockItem::UndatedStockItem(string Name,
  short InStock, short Price, short MinimumStock,
  short MinimumReorder, string Distributor, string UPC)
: StockItem(1),
  m_InStock(InStock),
  m_Price(Price),
  m_MinimumStock(MinimumStock),
  m_MinimumReorder(MinimumReorder),
  m_Name(Name),
  m_Distributor(Distributor),
  m_UPC(UPC)
{
}

void UndatedStockItem::FormattedDisplay(ostream& os)
{
  os << "Name: ";
  os << m_Name << endl;
  os << "Number in stock: ";
  os << m_InStock << endl;
  os << "Price: ";
  os << m_Price << endl;
  os << "Minimum stock: ";
  os << m_MinimumStock << endl;
  os << "Reorder quantity: ";
  os << m_MinimumReorder << endl;
  os << "Distributor: ";
  os << m_Distributor << endl;
  os << "UPC: ";
  os << m_UPC << endl;
  os << endl;
}

ostream& UndatedStockItem::Write(ostream& os)
{
  os << 0 << endl;
  os << m_Name << endl;
  os << m_InStock << endl;
  os << m_Price << endl;
  os << m_MinimumStock << endl;
  os << m_MinimumReorder << endl;
  os << m_Distributor << endl;
  os << m_UPC << endl;

  return os;
}

void UndatedStockItem::Reorder(ostream& os)
{
  short ReorderAmount;

  if (m_InStock < m_MinimumStock)
    {
    ReorderAmount = m_MinimumStock-m_InStock;
      if (ReorderAmount < m_MinimumReorder)
        ReorderAmount = m_MinimumReorder;
    os << "Reorder " << ReorderAmount;
    os << " units of " << m_Name << " with UPC ";
    os << m_UPC << " from " << m_Distributor << endl;
    }
}

bool UndatedStockItem::CheckUPC(string UPC)
{
  return (UPC == m_UPC);
}

short UndatedStockItem::GetInventory()
{
  return m_InStock;
}

void UndatedStockItem::DeductSaleFromInventory(
short QuantitySold)
{
  m_InStock -= QuantitySold;
}

string UndatedStockItem::GetName()
{
  return m_Name;
}


// DatedStockItem member functions

DatedStockItem::DatedStockItem(string Name, short InStock,
short Price, short MinimumStock, short MinimumReorder,
string Distributor, string UPC, string Expires)
: UndatedStockItem(Name,InStock,Price,MinimumStock,
  MinimumReorder,Distributor,UPC),
  m_Expires(Expires)
{
}

ostream& DatedStockItem::Write(ostream& os)
{
  os << m_Expires << endl;
  os << m_Name << endl;
  os << m_InStock << endl;
  os << m_Price << endl;
  os << m_MinimumStock << endl;
  os << m_MinimumReorder << endl;
  os << m_Distributor << endl;
  os << m_UPC << endl;

  return os;
}

void DatedStockItem::FormattedDisplay(ostream& os)
{
  os << "Expiration date: ";
  os << m_Expires << endl;
  os << "Name: ";
  os << m_Name << endl;
  os << "Number in stock: ";
  os << m_InStock << endl;
  os << "Price: ";
  os << m_Price << endl;
  os << "Minimum stock: ";
  os << m_MinimumStock << endl;
  os << "Reorder quantity: ";
  os << m_MinimumReorder << endl;
  os << "Distributor: ";
  os << m_Distributor << endl;
  os << "UPC: ";
  os << m_UPC << endl;
  os << endl;
}
string DatedStockItem::Today()
{
   struct date d;
   unsigned short year;
   unsigned short day;
   unsigned short month;
   string TodaysDate;
   stringstream FormatStream;

   getdate(&d);
   year = d.da_year;
   day = d.da_day;
   month = d.da_mon;

   FormatStream << setfill('0') << setw(4) << year <<
    setw(2) << month << setw(2) << day;
   FormatStream.seekg(0);
   FormatStream >> TodaysDate;

   return TodaysDate;
}

void DatedStockItem::Reorder(ostream& os)
{
  if (m_Expires < Today())
    {
    os << "Return " << m_InStock << " units of " << m_Name;
    os << " with UPC " << m_UPC;
    os << " to " << m_Distributor << endl;
    m_InStock = 0;
    }

  UndatedStockItem::Reorder(os);
}

Let's start our examination of this new StockItem class by looking at the implementation of operator << in Figure 10.25.

Figure 10.25. Safe polymorphism: The implementation of operator << for a polymorphic StockItem (from codeitemp.cpp)
ostream& operator << (ostream& os, const StockItem& Item)
{
  return Item.m_Worker->Write(os);
}

At first glance, this isn't particularly complicated. It just calls a function named Write via the StockItem* member variable m_Worker and returns the result from Write to its caller. But what does that m_Worker pointer point to?

This is the key to the implementation of polymorphic objects. The pointer m_Worker points to either a DatedStockItem or an UndatedStockItem depending on whether the object was created with or without an expiration date, respectively. Since the type of object that m_Worker points to is determined during program execution rather than when the program is compiled, the actual version of the Write function called by operator << varies accordingly, as it does with any type of polymorphism. The difference between this version of StockItem and the one described earlier in this chapter is that the pointer is used only in the implementation of StockItem rather than being accessible to the user of the class. This allows us to prevent the plague of memory allocation errors associated with pointer manipulation in the application program.

Susan had a question about the syntax of the call to the Write function.

Susan: What does the -> mean in that line?

Steve: It means to call the function on the right of the -> for the object pointed to by the pointer on the left of the ->. It's exactly like the . operator, except that operator has an object on its left instead of a pointer.

Before we get into the details of creating an object of the type defined by this new version of StockItem, we'll look at a couple of diagrams of the StockItem variables from the example program in Figure 10.21 to see exactly how this “internal polymorphism” works. First, Figure 10.26 shows a possible layout of the StockItem object, x, which is the argument to operator << in the statement cout << x;.

Figure 10.26. Safe polymorphism: A polymorphic StockItem object with no date


Let's trace the execution of the statement return Item.m_Worker->Write(os); from operator << (Figure 10.25 on page 706) when it is executed to display the value of the StockItem x (as a result of the statement cout << x; in the example program in Figure 10.21 on page 692). In step 1, the pointer m_Worker is followed to location 12321000, the address of the beginning of the UndatedStockItem worker object that will handle the operations of the StockItem manager class object. This location contains the address of the vtable for the worker object. In this case, the worker object is an UndatedStockItem object, so the vtable is the one for the UndatedStockItem class.

In our diagram that vtable is at location 12380000, so in step 2 we follow the vtable pointer to find the address of the Write function. The diagram makes the assumption that the Write function is the second virtual function defined in the StockItem class, so in step 3 we fetch the contents of the second entry in the vtable, which is 12390900. That is the address of the Write function that will be executed here.

Figure 10.27 shows a possible layout of the StockItem object y that is the argument to operator << in the statement cout << y;.

Figure 10.27. Safe polymorphism: A polymorphic StockItem object with a date


Susan had a number of questions about Figure 10.27.

Susan: What in Figure 10.27 is the StockItem? I can't tell exactly what it is supposed to be, just the vtable address, m_Worker, and m_Count? That's it, huh?

Steve: Yes.

Susan: What are all those member functions in step 3 supposed to be? I don't know where they're coming from.

Steve: They're from the class interface for the new version of StockItem, which is listed in Figure 10.22.

Susan: Why is there an UndatedStockItem listed along the side of the DatedStockItem thingy?

Steve: Since DatedStockItem is derived from UndatedStockItem, every DatedStockItem has an UndatedStockItem in it, just as every UndatedStockItem has a StockItem in it because UndatedStockItem is derived from StockItem.

Susan: Where is the UndatedStockItem now?

Steve: The only UndatedStockItem in Figure 10.27 is the base class part of the DatedStockItem worker object.

Susan: I still can't see how these objects look.

Steve: I think we need a diagram here. Take a look at Figure 10.28 and let me know if that helps.

Figure 10.28. A simplified version of the structure of a DatedStockItem object

Susan: Yes, it does.

Now that we've cleared that up, let's trace how the line return Item.m_Worker->Write(os); is executed to display the value of the StockItem y (as a result of the statement cout << y; in the example program in Figure 10.21). In step 1, the pointer m_Worker is followed to location 22321000, the address of the worker object that will handle the operations of the StockItem manager class object. This location contains the address of the vtable for the worker object. In this case, the worker object is a DatedStockItem object, so the vtable is the one for the DatedStockItem class. In our diagram, that vtable is at location 22380000, so in step 2 we follow the vtable pointer to find the address of the Write function. As before, the diagram makes the assumption that the Write function is the second virtual function defined in the StockItem class, so in step 3 we fetch the contents of the second entry in the vtable, which is 22390900. That is the address of the Write function that will be executed in this case.

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

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