本章前面讨论了单一继承,即一个类是从一个基类派生来的。一个类也可以从多个基类派生而来,这种派生称为“多重继承”(multiPle inheritance)。多重继承意味着一个派生类可以继承多个基类的成员,这种强大的功能支持了软件的复用性,但可能会引起大量的歧义性问题。
多重继承使用得好可具有强大的功能。当新类型与两个或多个现有类型之间存在”是”关系时(即类型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
在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++书籍对此有详细的论述。
多重继承是个强大的功能,但可能增加系统的复杂性。使用多重继承的系统需要更加认真设计,能用单一继承时应尽量使用单一继承。