当前位置: 首页 > 文档资料 > C++大学教程 >

9.15 多重继承

优质
小牛编辑
136浏览
2023-12-01

本章前面讨论了单一继承,即一个类是从一个基类派生来的。一个类也可以从多个基类派生而来,这种派生称为“多重继承”(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)的构造函数。同样,按指定的继承顺序调用基类构造函数,而不是按构造函数出现的顺序调用。如果成员初始化值列表中不显式调用基类构造函数,则隐式调用基类的默认构造函数。

1 // Fig. 9.11: basel.h
2 // Definition of class Basel
3 #ifndef BASE1_H
4 #define BASE1_H
5
6 class Base1 {
7 public:
8 Base1( int x ) { value = x; }
9 int getData() const { return value; }
10 protected: // accessible to derived classes
11 int value; // inherited by derived class
12 };
13
14 #endif
15 // Fig. 9.11: base2.h
16 // Definition of class Base2
17 #ifndef BASE2_H
18 #define BASE2_H
19
20 class Base2 {
21 public:
22 Base2( char c ) { letter = c; }
23 char getData() const { return letter; }
24 protected: // accessible to derived classes
25 char letter; // inherited by derived class
26 };
27
28 #endif
29 // Fig. 9.11: derived.h
30 // Definition of class Derived which inherits
31 // multiple base classes (Basel and Base2).
32 #ifndef DERIVED_H
33 #define DERIVED_H
34
35 #include "base1.h"
36 #include "base2.h"
37
38 // multiple inheritance
39 class Derived : public Base1, public Base2 {
40 friend ostream &operator<<( ostream &, const Derived & );
41
42 public:
43 Derived( int, char, double };
44 double getReal() const;
45
46 private:
47 double real; // derived class's private data
48 };
49
50 #endif
51 // Fig. 9.11: derived.cpp
52 // Member function definitions for class Derived
53 #include <iostream.h>
54 #include "derived.h"
55
56 // Constructor for Derived calls constructors for
57 // class Basel and class Base2.
58 // Use member initializers to call base-class constructors
59 Derived::Derived( int i, char C, double f )
60 : Base1( i ), Base2( c ), real ( f ) {
61
62 // Return the value of real
63 double Derlved::getRealO const { return real; }
64
65 // Display all the data members of Derived
66 ostream &operator<<( ostream &output, const Derived &d )
67 {
68 output <<" Integer: "<< d.value
69 << "\n Character: "<< d.letter
70 << "\nReal number: "<< d.real;
71
72 return output; // enables cascaded calls
73 }
74 / Fig. 9.11: fig09_ll.cpp
75 // Driver for multiple inheritance example
76 #include <iostream.h>
77 #include "base1.h"
78 #include "base2.h"
79 #include "derived.h"
80
81 int main()
82 {
83 Base1 b1( 10 ), *base1Ptr = 0; // create Basel object
84 Base2 b2( 'Z' ), *base2Ptr = 0; // create Base2 object
85 Derived d( 7, 'A', 3.5 ); // create Derived object
86
87 // print data members of base class objects
88 cout << "Object b1 contains integer "<< b1.getData()
89 << "\nObject b2 contains character" << b2.getData()
90 << "\nObject d contains:\n" << d << "\n\n";
91
92 // print data members of derived class object
93 // scope resolution operator resolves getData ambiguity
94 cout << "Data members of Derived can be"
95 << "accessed imdividually:"
96 << "In Integer: "<< d. Base1::getData()
97 << "\n Character: << d. Base2::getData()
98 << "\nReal number: "<< d.getReal() << "\n\n";
99
100 cout << "Derived can be treated as an"
101 << "object of either base class:In";
102
103 // treat Derived as a Basel object
104 base1Ptr = &d;
105 cout << "base1Ptr->getData() yields"
106 << base1Ptr->getData() << '\n';
107
108 // treat Derived as a Base2 object
109 base2Ptr = &d;
110 cout << "base2Ptr->getData() yields"
111 << base2Ptr->getData() << endl;
112
113 return 0;
114 }

输出结果:

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