Motto: a public derived class should be a specialization of its base class
In one respect, the program has lost ground in the transformation.
The original program specified only once, in Card::rebate(), that
the default rebate for cards i s45. With the current definition of
Component, the values of all non-zero rebates must be specified in
the object declarations. The program has no place to record a default
rebate specifically for cards. The program does need to distinguish
cards from monitors. Inheritance can provide appropriate specialization
with distinct constructors for cards and monitors.
We must reintroduce the classes Card and Monitor to provide constructors with the appropriate rebate defaults.
The specialization in the derived classes is limited to their constructors. The default rebates are specified as default argument values to the constructors for Card and Monitor.
#include <iostream> using namespace std; enum CARD {CDROM, TAPE, NETWORK }; enum MONITOR { MONO, COLOR }; class Component { public: Component( int p, char *n, int r = 0); int netPrice(); int price() { return m_price; } char *name() { return m_name; } int rebate() { return m_rebate; } private: int m_price; char *m_name; int m_rebate; }; Component::Component( int p, char *n, int r) : m_price(p), m_name(n), m_rebate(r) { } int Component::netPrice() { return price() - rebate(); } class Card : public Component { public: Card( int p, char *n, int r = 45) : Component( p, n, r) { } }; class Monitor : public Component { public: Monitor( int p, char *n, int r = 0) : Component( p, n, r) { } };
Generally, public inheritance is used when the derived class is a specialization of the base class, that is, when the classes exhibit the "is a kind of" relationship. A Card is a kind of Component; a Monitor is a kind of Component. In this case, the specialization applies only during construction - once constructed, all Component objects behave uniformly. Limiting the variation in the derived classes to initialization - constructor specialization - is a legitimate use of inheritance.
It is interesting to note, that Card and Monitor now differ in a "default value", implemented as a constructor default argument value. In the original version of the program, Card and Monitor differed in a "default behaviour", implemented as a virtual function.
Card Network(600, "Network"); Card CDRom(1500, "CDRom", 135); Card Tape(1000, "Tape"); Monitor Color(1500, "Color"); Monitor Monochrome(500, "Mono");
In place of five Component objects, there are now three Card objects and two Monitor objects. Only one rebate value need be specified; only the CDRom object deviates from its default.
class Computer { public: Computer( Card *c, Monitor *m ); int netPrice(); void print(); private: Card *card; Monitor *mon; };
The reintroduction of Card and Monitor can simplify the program in other way. The arguments to Computer::Computer specify one CARD and one MONITOR value. The enumerations place a level of indirection between the constructor and the information it needs, without adding any flexibility. The constructor really needs the Component objects and has to map each enumeration value to an object.
Maintaining this mapping is complicated. To add another Monitor for example GreyScale, the programmer has to
Card and Monitor are distinct types. If Computer::Computer takes pointers to objects as its arguments, the enumerations and the switch statements can be removed.
Computer::Computer( Card *c, Monitor *m ) { card = c; mon = m; } int Computer::netPrice() { return mon->netPrice() + card->netPrice(); } void Computer::print() { cout << "Configuration = " << card->name() << ", " << mon->name() << ", net price = " << netPrice() << endl; }
Here you must modify the client code: you construct Computer with the addresses of the objects rather than the enumerators
int main() { Computer nm( &Network, &Monochrome ); Computer cm( &CDRom, &Monochrome ); Computer tm( &Tape, &Monochrome ); Computer nc( &Network, &Color ); Computer cc( &CDRom, &Color ); Computer tc( &Tape, &Color ); nm.print(); cm.print(); tm.print(); nc.print(); cc.print(); tc.print(); return 0; }