ITEEDU

9.15 多重继承

本章前面讨论了单一继承,即一个类是从一个基类派生来的。一个类也可以从多个基类派生而来,这种派生称为“多重继承”(multiPle inheritance)。多重继承意味着一个派生类可以继承多个基类的成员,这种强大的功能支持了软件的复用性,但可能会引起大量的歧义性问题。

编程技巧9.1

多重继承使用得好可具有强大的功能。当新类型与两个或多个现有类型之间存在”是”关系时(即类型A“是”类型B并且也“是”类型c)应该使用多重继承。

图9.11中的程序是一个多重继承的例子。类Base1包含一个protected数据成员int value,还包含设置value值的构造函数和返回value值的public成员函数getData。

类Base2和类Base1相似,只不过它的protected数据成员是char letter。类Base2也包含一个Public成员函数getData,但是该函数返回的是char letter的值。

类Derivcd通过多重继承机制继承了类Base1和类Base2,它有一个private数据成员float real和一个读取float real的public成员函数getReal。

多重继承是非常直接的,即在class derived后的冒号(:)之后跟上用逗号分开的公有基类列表。还可以看到,构造函数Derived显式地调用了每个基类(即Bae1和Base2)的构造函数。同样,按指定的继承顺序调用基类构造函数,而不是按构造函数出现的顺序调用。如果成员初始化值列表中不显式调用基类构造函数,则隐式调用基类的默认构造函数。

 // Fig. 9.11: basel.h
 // Definition of class Basel
 #ifndef BASE1_H
 #define BASE1_H
 class Base1 {
 public:
   Base1( int x ) { value = x; }
   int getData() const { return value; }
 protected:     // accessible to derived classes
   int value;  // inherited by derived class
 };
 #endif
 // Fig. 9.11: base2.h
 // Definition of class Base2
 #ifndef BASE2_H
 #define BASE2_H
 class Base2 {
 public:
   Base2( char c ) { letter = c; }
   char getData() const { return letter; }
 protected:      // accessible to derived classes
   char letter;  // inherited by derived class
 };
 #endif
 // Fig. 9.11: derived.h
 // Definition of class Derived which inherits
 // multiple base classes (Basel and Base2).
 #ifndef DERIVED_H
 #define DERIVED_H
 #include "base1.h"
 #include "base2.h"
 // multiple inheritance
 class Derived : public Base1, public Base2 {
   friend ostream &operator<<( ostream &, const Derived & );
 public:
   Derived( int, char, double };
   double getReal() const;
 private:
   double real;  // derived class's private data
 };
 #endif
 // Fig. 9.11: derived.cpp
 // Member function definitions for class Derived
 #include < iostream.h>
 #include "derived.h"
 // Constructor for Derived calls constructors for
 // class Basel and class Base2.
 // Use member initializers to call base-class constructors
 Derived::Derived( int i, char C, double f )
   : Base1( i ), Base2( c ), real ( f ) {
 // Return the value of real
 double Derlved::getRealO const { return real; }
 // Display all the data members of Derived
 ostream &operator<<( ostream &output, const Derived &d )
 {
   output <<"   Integer: "<< d.value
          << "\n Character: "<< d.letter
          << "\nReal number: "<< d.real;
   return output;  // enables cascaded calls
 }
 / Fig. 9.11: fig09_ll.cpp
 // Driver for multiple inheritance example
 #include < iostream.h>
 #include "base1.h"
 #include "base2.h"
 #include "derived.h"
 int main()
 {
   Base1 b1( 10 ), *base1Ptr = 0;  // create Basel object
   Base2 b2( 'Z' ), *base2Ptr = 0; // create Base2 object
   Derived d( 7, 'A', 3.5 );      // create Derived object
   // print data members of base class objects
   cout << "Object b1 contains integer "<< b1.getData()
        << "\nObject b2 contains character" << b2.getData()
        << "\nObject d contains:\n" << d << "\n\n";
 // print data members of derived class object
 // scope resolution operator resolves getData ambiguity
   cout << "Data members of Derived can be"
        << "accessed imdividually:"
        << "In   Integer: "<< d. Base1::getData()
        << "\n Character:  << d. Base2::getData()
        << "\nReal number: "<< d.getReal() << "\n\n";
  cout << "Derived can be treated as an"
       << "object of either base class:In";
 // treat Derived as a Basel object
  base1Ptr = &d;
  cout << "base1Ptr->getData() yields"
       << base1Ptr->getData() << '\n';
 // treat Derived as a Base2 object
  base2Ptr = &d;
  cout << "base2Ptr->getData() yields"
       << base2Ptr->getData() << endl;
  return 0;
 }

输出结果:

object bl contains integer 10

object b2 contains character z

object d 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:

baselPtr->getDataO yields 7

base2Ptr->getData() yields A

图9.11 多重继承的程序

在Derived中重载的流插入运算符通过派生类对象d用圆点表示法打印value、letter和real的值。因为该运算符函数是类Derived的友元,所以operator<<可以直接访问类Derived的private数据成员real,还能访问Base1和Base2的protected数据成员value和letter。

下面探讨一下main函数中的驱动程序。程序中首先建立了类Base1的对象b1和类Base2的对象b2,并将它们分别初始化为int类型的值10和char类型的值'z',然后建立类Derived的对象d并将其初始化成包括int类型的值7、char类型的值'A'和float类型的值3.5。

通过调用每个对象的getData函数打印每个基类对象的内容。尽管有两种getData函数,但是因为直接引用了对象b1和b2的getData函数版本,因此对它们的调用没有歧义性问题。

接下来用静态关联打印出Derived的对象d的内容。因为该对象包含两个getData函数,一个是从类Base1继承来的,另一个是从Base2继承来的,所以存在着歧义性问题。用二元作用域运算符很容易解决这个问题。例如,d.Base1::getData()打印了int类型的value值,d.Base2::getData()打印了char类型的letter值。调用d.getReal()打印float类型的real值不存在歧义性问题。然后演示了单一继承的“是”关系也适用于多重继承。程序中把派生类对象d的地址赋给了基类指针Base1Ptr,并用该指针调用Base1的成员函数getData打印出int value值。之后又把d的地址赋给基类指针Base2Ptr,并用该指针调用Base2的成员函数getData打印出charletter的值。

这个例子展示了多重继承的机制并介绍了一种简单的歧义性问题。多重继承是一个很复杂的话

题,许多高级C++书籍对此有详细的论述。

软件工程视点9.12

多重继承是个强大的功能,但可能增加系统的复杂性。使用多重继承的系统需要更加认真设计,能用单一继承时应尽量使用单一继承。