class Stack { public: Stack() : count(0) {} void push(int i) { assert(count < 100); count++; data[count]=i; } int pop() { assert(count > 0); count--; return data[count]; } private: size_t count; int data[100]; };Suppose we decide to implement a top function. We might write:
class StackWithTop { public: Stack() : count(0) {} void push(int i) { assert(count < 100); count++; data[count]=i; } int pop() { assert(count > 0); count--; return data[count]; } int top() { assert(count > 0); return data[count-1]; } private: size_t count; int data[100]; };Wait! There's a bug in the original code! We wanted:
class Stack { public: Stack() : count(0) {} void push(int i) { assert(count < 100); data[count]=i; count++; } int pop() { assert(count > 0); count--; return data[count]; } private: size_t count; int data[100]; };We can either recopy and rename, or change the mistake again. Either way, we have to do something redundantly.
class StackWithTop { public: Stack() : count(0) {} void push(int i) { assert(count < 100); data[count]=i; count++; } int pop() { assert(count > 0); count--; return data[count]; } int top() { assert(count > 0); return data[count-1]; } private: size_t count; int data[100]; };
Revised goal: Build new, but similar, data structures from old and avoid redundancy.
We have already seen several techniques for reusing code:class StackWithTop : public Stack { public: int top() { assert(count > 0); return data[count-1]; } };Of course we need to make a small change in the class Stack to make this valid:
class Stack { public: Stack() : count(0) {} void push(int i) { assert(count < 100); data[count]=i; count++; } int pop() { assert(count > 0); count--; return data[count]; } protected: size_t count; int data[100]; };
Inheritance comes in many flavors, each with its own subtleties. (Entire books and courses focus on inheritance.) Here we just consider one such flavor --- public inheritance --- and even so, only consider it in a simple form.
Public inheritance is most often used to capture the is a relationship.
We say that a derived class (subclass) inherits from a base class (superclass). A derived class inherits all public members of its base class as public members. A derived class may access any protected members of its base class. Protected members remain invisible to the outside world.
class Foo { public: int pub; protected: int pro; private: int pri; }; class Bar : public Foo { public: void bogus(); } void Bar::bogus() { pub = 3; // ok pro = 2; // ok pri = 1; // ERROR! no access to private members of Foo } Bar b; cout << b.pub; // ok cout << b.pro; // ERROR: protected members can't be used outside class cout << b.pri; // ERROR
Rotating a circle is simpler than rotating other shapes. We'd like to be able to override the base version of rotate so that we circle's rotate is more efficient. We can do that by redefining the rotate function in the circle class.
Warning: never override the signature of a method (member function). Preserve: number of arguments, argument type, and return type.
class Point { public: Point(double yy, double xx) : y(yy), x(xx) {} Point(const Point& source); // copy con protected: double y,x; }; enum color {BLUE, GREEN, RED}; class ColorPoint : public Point { public: ColorPoint(double yy, double xx, color cc) : Point(yy, xx), c(cc) {} ColorPoint(const ColorPoint& source); // copy con private: color c; }; Point p(1.4, 2.1); ColorPoint cp(3.4, 1.5, BLUE); Point p1(p); // ok ColorPoint cp1(cp); // ok Point p2(cp); // ok, p2 forgets about cp's color ColorPoint cp2(p); // ERROR! what would cp2's color be?
template <class Item> class OrderedList : public List<Item> { public: void insert(const Item& entry); // override List's insert }; template<class Item> void OrderedList<Item>::insert(const Item& entry) { assert(!isFull()); size_t i; for(i=0; i < count && data[i] < entry; i++); for(size_t j=i; j < count; j++) data[j+1] = data[j]; data[i] = entry; count++; }Now, we can consider using inheritance to build more efficient binary search trees.