21.7. Multiple Inheritance

In Chapters 11 and 12, we discussed single inheritance, in which each class is derived from exactly one base class. In C++, a class may be derived from more than one base class—a technique known as multiple inheritance in which a derived class inherits the members of two or more base classes. This powerful capability encourages interesting forms of software reuse but can cause a variety of ambiguity problems. Multiple inheritance is a difficult concept that should be used only by experienced programmers. In fact, some of the problems associated with multiple inheritance are so subtle that newer programming languages, such as Java and C#, do not enable a class to derive from more than one base class.


Image Software Engineering Observation 21.3

Great care is required in the design of a system to use multiple inheritance properly; it should not be used when single inheritance and/or composition will do the job.


A common problem with multiple inheritance is that each of the base classes might contain data members or member functions that have the same name. This can lead to ambiguity problems when you attempt to compile. Consider the multiple-inheritance example (Figs. 21.721.11). Class Base1 (Fig. 21.7) contains one protected int data member—value (line 20), a constructor (lines 10–13) that sets value and public member function getData (lines 15–18) that returns value.


 1   // Fig. 21.7: Base1.h
 2   // Definition of class Base1
 3   #ifndef BASE1_H
 4   #define BASE1_H
 5
 6   // class Base1 definition
 7   class Base1
 8   {
 9   public:
10      Base1( int parameterValue )
11         : value( parameterValue )
12      {
13      } // end Base1 constructor
14
15      int getData() const
16      {
17         return value;
18      } // end function getData
19   protected: // accessible to derived classes
20      int value; // inherited by derived class
21   }; // end class Base1
22
23   #endif // BASE1_H


Fig. 21.7. Demonstrating multiple inheritance—Base1.h.

Class Base2 (Fig. 21.8) is similar to class Base1, except that its protected data is a char named letter (line 20). Like class Base1, Base2 has a public member function getData, but this function returns the value of char data member letter.


 1   // Fig. 21.8: Base2.h
 2   // Definition of class Base2
 3   #ifndef BASE2_H
 4   #define BASE2_H
 5
 6   // class Base2 definition
 7   class Base2
 8   {
 9   public:
10      Base2( char characterData )
11         : letter( characterData )
12      {
13      } // end Base2 constructor
14
15      char getData() const
16      {
17         return letter;
18      } // end function getData
19   protected: // accessible to derived classes
20      char letter; // inherited by derived class
21   }; // end class Base2
22
23   #endif // BASE2_H


Fig. 21.8. Demonstrating multiple inheritance—Base2.h.

Class Derived (Figs. 21.921.10) inherits from both class Base1 and class Base2 through multiple inheritance. Class Derived has a private data member of type double named real (Fig. 21.9, line 20), a constructor to initialize all the data of class Derived and a public member function getReal that returns the value of double variable real.


 1   // Fig. 21.9: Derived.h
 2   // Definition of class Derived which inherits
 3   // multiple base classes (Base1 and Base2).
 4   #ifndef DERIVED_H
 5   #define DERIVED_H
 6
 7   #include <iostream>
 8   #include "Base1.h"
 9   #include "Base2.h"
10   using namespace std;
11
12   // class Derived definition
13   class Derived : public Base1, public Base2
14   {
15      friend ostream &operator<<( ostream &, const Derived & );
16   public:
17      Derived( int, char, double );
18      double getReal() const;
19   private:
20      double real; // derived class's private data
21   }; // end class Derived
22
23   #endif // DERIVED_H


Fig. 21.9. Demonstrating multiple inheritance—Derived.h.


 1   // Fig. 21.10: Derived.cpp
 2   // Member-function definitions for class Derived
 3   #include "Derived.h"
 4
 5   // constructor for Derived calls constructors for              
 6   // class Base1 and class Base2.                                
 7   // use member initializers to call base-class constructors     
 8   Derived::Derived( int integer, char character, double double1 )
 9      : Base1( integer ), Base2( character ), real( double1 ) { } 
10
11   // return real
12   double Derived::getReal() const
13   {
14      return real;
15   } // end function getReal
16
17   // display all data members of Derived
18   ostream &operator<<( ostream &output, const Derived &derived )
19   {
20      output << "    Integer: " << derived.value << "   Character: "
21           << derived.letter << " Real number: " << derived.real;
22      return output; // enables cascaded calls
23   } // end operator<<


Fig. 21.10. Demonstrating multiple inheritance—Derived.cpp.

To indicate multiple inheritance (in Fig. 21.9) we follow the colon (:) after class Derived with a comma-separated list of base classes (line 13). In Fig. 21.10, notice that constructor Derived explicitly calls base-class constructors for each of its base classes—Base1 and Base2—using the member-initializer syntax (line 9). The base-class constructors are called in the order that the inheritance is specified, not in the order in which their constructors are mentioned. Also, if the base-class constructors are not explicitly called in the member-initializer list, their default constructors will be called implicitly.

The overloaded stream insertion operator (Fig. 21.10, lines 18–23) uses its second parameter—a reference to a Derived object—to display a Derived object’s data. This operator function is a friend of Derived, so operator<< can directly access all of class Derived’s protected and private members, including the protected data member value (inherited from class Base1), protected data member letter (inherited from class Base2) and private data member real (declared in class Derived).

Now let’s examine the main function (Fig. 21.11) that tests the classes in Figs. 21.721.10. Line 11 creates Base1 object base1 and initializes it to the int value 10. Line 12 creates Base2 object base2 and initializes it to the char value 'Z'. Line 13 creates Derived object derived and initializes it to contain the int value 7, the char value 'A' and the double value 3.5.


 1   // Fig. 21.11: fig21_11.cpp
 2   // Driver for multiple-inheritance example.
 3   #include <iostream>
 4   #include "Base1.h"
 5   #include "Base2.h"
 6   #include "Derived.h"
 7   using namespace std;
 8
 9   int main()
10   {
11      Base1 base1( 10 ); // create Base1 object
12      Base2 base2( 'Z' ); // create Base2 object
13      Derived derived( 7, 'A', 3.5 ); // create Derived object
14
15      // print data members of base-class objects
16      cout << "Object base1 contains integer " << base1.getData()
17         << " Object base2 contains character " << base2.getData()
18         << " Object derived contains: " << derived << " ";
19
20      // print data members of derived-class object
21      // scope resolution operator resolves getData ambiguity
22      cout << "Data members of Derived can be accessed individually:"
23         << "     Integer: " << derived.Base1::getData()
24         << "   Character: " << derived.Base2::getData()
25         << " Real number: " << derived.getReal() << " ";
26      cout << "Derived can be treated as an object of either base class: ";
27
28      // treat Derived as a Base1 object
29      Base1 *base1Ptr = &derived;
30      cout << "base1Ptr->getData() yields " << base1Ptr->getData() << ' ';
31
32      // treat Derived as a Base2 object
33      Base2 *base2Ptr = &derived;
34      cout << "base2Ptr->getData() yields " << base2Ptr->getData() << endl;
35   } // end main


Object base1 contains integer 10
Object base2 contains character Z
Object derived contains:
    Integer: 7
  Character: A
Real number: 3.5

Data members of Derived can be accessed individually:
    Integer: 7
  Character: A
Real number: 3.5

Derived can be treated as an object of either base class:
base1Ptr->getData() yields 7
base2Ptr->getData() yields A


Fig. 21.11. Demonstrating multiple inheritance.

Lines 16–18 display each object’s data values. For objects base1 and base2, we invoke each object’s getData member function. Even though there are two getData functions in this example, the calls are not ambiguous. In line 16, the compiler knows that base1 is an object of class Base1, so class Base1’s getData is called. In line 17, the compiler knows that base2 is an object of class Base2, so class Base2’s getData is called. Line 18 displays the contents of object derived using the overloaded stream insertion operator.

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

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