Module I - Inheritance

Multiple Inheritance

"There is debate as to whether multiple inheritance can be implemented simply and without ambiguity" Wikipedia (2007).

Multiple Inheritance | Virtual Inheritance | Practice | Exercise



Multiple inheritance provides flexibility in class hierarchies.  With multiple inheritance, a class can be derived from several base classes.  Examples of multiply-inherited classes include: iostream (istream, ostream), omnivores (carnivores, herbivores), amphibious car (car, boat), clock radio (clock, radio), cellphone (transmitter, receiver), and student-teacher (student, teacher). 


Multiple Inheritance

A declaration of multiple inheritance takes the form

 class Derived : base class list {
 ...
 };

The base class list is a comma-separated list of the names of the direct base classes.  There is no limit on the number of base classes in this list, but a name cannot be repeated.  Each class included in the list should be preceded by its own access modifier.  The access modifier applies only to the base class immediately following the modifier and not to any subsequent base classes in the list.  In the absence of an access modifier, the compiler assumes private access. 

For example, let us derive a ColouredBox class from a Box class and a ColouredShape class. 

Consider the following declarations for the two base classes.  The declaration for the Box class is


 // A Box
 // Box.h
 // Jun 9 2007

 const int MC = 30;

 class Box {
     char name[MC + 1];
     int hght;
     int wdth;
     int dpth;
 protected:
     void displaySpecific() const;
 public:
     Box(const char* s, int h, int w, int d); 
     virtual void display() const;
 };

The declaration for the ColouredShape class is


 // A Shape with a Colour
 // ColouredShape.h
 // Jun 9 2007

 const int MC = 30;

 class ColouredShape {
     char name[MC + 1];
     long color;
 protected:
     void displaySpecific() const;
 public:
     ColouredShape(const char* s = "", long c = 0); 
     virtual void display() const;
 };

The accompanying implementation file for the Box class is 


 // A Box
 // Box.cpp
 // Jun 9 2007

 #include <iostream>
 #include "Box.h"
 using namespace std;

 Box::Box(const char* s, int h, int w, int d) : hght(h), wdth(w), dpth(d) { 
     strncpy(name, s, MC);
     name[MC] = '\0';
 }

 void Box::displaySpecific() const {
     cout << hght << " x " << wdth << " x " << dpth << ' '; 
 }

 virtual void Box::display() const {
     cout << name << ' ';
     displaySpecific();
 }

The accompanying implementation file for the ColouredShape class is 


 // A Shape With a Colour
 // ColouredShape.cpp
 // Jun 9 2007

 #include <iostream>
 #include "ColouredShape.h"
 using namespace std;

 ColouredShape::ColouredShape(const char* s, long c) : color(c) { 
     strncpy(name, s, MC);
     name[MC] = '\0';
 }

 void ColouredShape::displaySpecific() const {
     cout << hex << color << dec << ' ';
 }

 virtual void ColouredShape::display() const {
     cout << name << ' ';
     displaySpecific();
 }

The displaySpecific() functions are neither virtual nor public.  Each function is designed to be called only from the display() functions within the inheritance hierarchy. 

Let us derive a new class named ColouredBox from the Box class and the ColouredShape class.  The declaration for this new class is


 // A Box With a Colour
 // ColouredBox.h
 // Jun 9 2007

 #include "Box.h"
 #include "ColouredShape.h"

 class ColouredBox : public Box, public ColouredShape {
 public:
     ColouredBox(const char* s, int h, int w, int d, long c); 
     void display() const;
 };

The accompanying implementation file for the ColouredBox class is


 // A Box With a Colour
 // ColouredBox.cpp
 // Jun 9 2007

 #include "ColouredBox.h"

 ColouredBox::ColouredBox(const char* s, int h, int w, int d, long c) : 
     Box(s, h, w, d), ColouredShape(s, c) {}

 void ColouredBox::display() const {
     Box::display();
     ColouredShape::displaySpecific();
 }

The derived class constructor calls the base class constructors and passes to them the appropriate initialization values.  If the derived class constructor did not include calls to the base class constructors, the compiler would call the default constructors.  If any base class has a constructor that takes arguments, but does not include a no-argument constructor, then the compiler could not complete the construction of the object for that base class. 

In the derived display() function, the the call to the Box::display() function displays the box's name followed by its dimensions.  The subsequent call to the displaySpecific() function displays the colour but avoids re-displaying the name. 

Finally, the application code that uses this derived class along with the two base classes is


 // Multiple Inheritance
 // multiple.cpp
 // Jun 9 2007

 #include <iostream>
 #include "ColouredBox.h"
 using namespace std;

 int main() {
     ColouredBox cube("cube", 2, 2, 2, 0xccddccL); 
     Box box("box", 2, 3, 4);
     ColouredShape shape("shape", 0xddffddL);

     cube.display();
     cout << endl;
     box.display();
     cout << endl;
     shape.display();
     cout << endl;

     return 0;
 }















 cube 2 x 2 x 2 ccddcc 

 box 2 x 3 x 4

 shape ddffdd



 

Replicated Base Classes

The two base classes in the above example share name as an instance variable.  Let us introduce a further base class named Shape that holds the name and let us derive both ColouredShape and Box from this new base class. 

The declaration for the Shape class is


 // A Shape
 // Shape.h
 // Jun 9 2007

 const int MC = 30;

 class Shape {
     char name[MC + 1];
 public:
     Shape(const char* s = ""); 
     virtual void display() const;
 };

The accompanying implementation file for the Shape class is 


 // A Shape
 // Shape.cpp
 // Jun 9 2007

 #include <iostream>
 #include "Shape.h"
 using namespace std;

 Shape::Shape(const char* s) { 
     strncpy(name, s, MC);
     name[MC] = '\0';
 }

 virtual void Shape::display() const { 
     cout << name << ' ';
 }

This simplifies the Box and ColouredShape classes.  The declaration for the Box class becomes


 // A Box
 // Box.h
 // Jun 9 2007

 #include "Shape.h"

 class Box : public Shape {
     int hght;
     int wdth;
     int dpth;
 protected:
     void displaySpecific() const;
 public:
     Box(const char* s, int h, int w, int d); 
     virtual void display() const;
 };

The declaration for the ColouredShape class becomes


 // A Shape with a Colour
 // ColouredShape.h
 // Jun 9 2007

 #include "Shape.h"

 class ColouredShape : public Shape { 
     long color;
 protected:
     void displaySpecific() const;
 public:
     ColouredShape(const char* s = "", long c = 0); 
     virtual void display() const;
 };

The accompanying implementation file for the Box class becomes 


 // A Box
 // Box.cpp
 // Jun 9 2007

 #include <iostream>
 #include "Box.h"
 using namespace std;

 Box::Box(const char* s, int h, int w, int d) :
  Shape(s), hght(h), wdth(w), dpth(d) {}

 void Box::displaySpecific() const {
     cout << hght << " x " << wdth << " x " << dpth << ' '; 
 }

 virtual void Box::display() const {
     Shape::display();
     displaySpecific();
 }

The accompanying implementation file for the ColouredShape class is 


 // A Shape With a Colour
 // ColouredShape.cpp
 // Jun 9 2007

 #include <iostream>
 #include "ColouredShape.h"
 using namespace std;

 ColouredShape::ColouredShape(const char* s, long c) : 
  Shape(s), color(c) {}

 void ColouredShape::displaySpecific() const {
     cout << hex << color << dec << ' ';
 }

 virtual void ColouredShape::display() const {
     Shape::display();
     displaySpecific();
 }

Note that we have not changed the most derived class in this refinement.  Although ColouredBox cannot be derived from two Shape classes directly, a ColouredBox object does contain two Shape objects.  Such indirect derivation from the same base class is admissible.  Note that this design duplicates the Shape sub-object.

When a class is derived from different classes that share a common base class, the possibility of ambiguity arises.  For instance, adding the call Shape::display() to the most derived display function would generate a compiler error, since the compiler has no way of determining which Shape sub-object should be displayed.  We need to code carefully to avoid such ambiguities.  In this example, the call to the display() function on the Shape sub-object is from within the ColouredShape or Box class, and hence there is no ambiguity. 


Virtual Inheritance

To avoid duplication of the instance of the base class, the Box and ColouredShape object can share the same Shape object. 

To implement this design, we identify the Shape class as virtual.  The declaration for the Box class becomes


 // A Box
 // Box.h
 // Jun 9 2007

 #include "Shape.h"

 class Box : virtual public Shape {
     int hght;
     int wdth;
     int dpth;
 protected:
     void displaySpecific() const;
 public:
     Box(const char* s, int h, int w, int d); 
     virtual void display() const;
 };

The declaration for the ColouredShape class becomes


 // A Shape with a Colour
 // ColouredShape.h
 // Jun 9 2007

 #include "Shape.h"

 class ColouredShape : virtual public Shape {
     long color;
 protected:
     void displaySpecific() const;
 public:
     ColouredShape(const char* s = "", long c = 0); 
     virtual void display() const;
 };

Every virtual base of a derived class is represented by the same shared object.  Conversely, every base class that is not specified as virtual is represented by its own object. 

The constructor for a virtual base class is called only once from the constructor for the complete object.  Here, the constructor for Shape is called implicitly from the constructor for ColouredBox


 // A Box With a Colour
 // ColouredBox.cpp
 // Jun 9 2007

 #include "ColouredBox.h"

 ColouredBox::ColouredBox(const char* s, int h, int w, int d, long c) : 
     Box(s, h, w, d), ColouredShape(s, c) {}

 void ColouredBox::display() const {
     Box::display();
     ColouredShape::displaySpecific();
 }

Note that the result of this call is an empty name for the object. 


 // Multiple Inheritance
 // multiple.cpp
 // Jun 9 2007

 #include <iostream>
 #include "ColouredBox.h"
 using namespace std;

 int main() {
     ColouredBox cube("cube", 2, 2, 2, 0xccddccL); 
     Box box("box", 2, 3, 4);
     ColouredShape shape("shape", 0xddffddL);

     cube.display();
     cout << endl;
     box.display();
     cout << endl;
     shape.display();
     cout << endl;

     return 0;
 }















  2 x 2 x 2 ccddcc 

 box 2 x 3 x 4

 shape ddffdd



 

To pass the specified name to the virtual base class, we add an explicit call to its constructor from the constructor for the complete object.  Moreover, for symmetry we introduce a direct call to the display function of the virtual base class.


 // A Box With a Colour
 // ColouredBox.cpp
 // Jun 9 2007

 #include "ColouredBox.h"

 ColouredBox::ColouredBox(const char* s, int h, int w, int d, long c) : 
     Shape(s), Box(s, h, w, d), ColouredShape(s, c) {}

 void ColouredBox::display() const {
     Shape::display();
     Box::displaySpecific();
     ColouredShape::displaySpecific();
 }

Then the result of the call to display() on the ColouredBox object displays the name that is stored in the Shape sub-object. 


 // Multiple Inheritance
 // multiple.cpp
 // Jun 9 2007

 #include <iostream>
 #include "ColouredBox.h"
 using namespace std;

 int main() {
     ColouredBox cube("cube", 2, 2, 2, 0xccddccL); 
     Box box("box", 2, 3, 4);
     ColouredShape shape("shape", 0xddffddL);

     cube.display();
     cout << endl;
     box.display();
     cout << endl;
     shape.display();
     cout << endl;

     return 0;
 }















 cube 2 x 2 x 2 ccddcc 

 box 2 x 3 x 4

 shape ddffdd



 

Finally, in order to prevent constructions of the base class alone, we identify the constructor as protected


 // A Shape
 // Shape.h
 // Jun 9 2007

 const int MC = 30;

 class Shape {
     char name[MC + 1];
 protected:
     Shape(const char* s = ""); 
 public:
     virtual void display() const;
 };


In-Class Practice

Try the practice problem in the Handout on Multiple Inheritance.


Exercise and Research




   Printer Friendly Version of this Page print this page     Top  Go Back to the Top of this Page
Previous Reading  Previous: Parametric Polymorphism Next: Abstract Base Classes   Next Reading


  Designed by Chris Szalwinski   Copying From This Site