C++多重继承与虚拟继承
本文只是粗浅讨论一下C++中的多重继承和虚拟继承。
多重继承中的构造函数和析构函数调用次序
我们先来看一下简单的例子:
1 #include <iostream> 2 using namespace std; 3 4 class A 5 { 6 private: 7 char idA; 8 9 public: 10 A(){ 11 idA = 'A'; 12 cout << "Constructor of A is called!" << endl; 13 } 14 ~A() { cout << "Destructor of A is called!" << endl; } 15 }; 16 17 class B : public A 18 { 19 private: 20 char idB; 21 22 public: 23 B(){ 24 idB = 'B'; 25 cout << "Constructor of B is called!" << endl; 26 } 27 ~B() { cout << "Destructor of B is called!" << endl; } 28 }; 29 30 class C : public A 31 { 32 private: 33 char idC; 34 35 public: 36 C(){ 37 idC = 'C'; 38 cout << "Constructor of C is called!" << endl; 39 } 40 ~C() { cout << "Destructor of C is called!" << endl; } 41 }; 42 43 class D : public B, public C 44 { 45 private: 46 char idD; 47 48 public: 49 D(){ 50 idD = 'D'; 51 cout << "Constructor of D is called!" << endl; 52 } 53 ~D() { cout << "Destructor of D is called!" << endl; } 54 }; 55 56 int main() 57 { 58 D d; 59 return 0; 60 }
上述程序的输出为:
由上边结果可以看出,析构函数调用次序跟构造函数是相反的。另外,构造函数调用次序跟类D继承B、C次序(public B, public C)相关。
可能我们也发现了,对于类D的实例d来说,它其实有两个重复的A实例。我们应该要去掉其中一个以节省空间。具体做法就是采用虚拟继承的方法:
1 class B : public virtual A 2 { 3 ... 4 }; 5 6 class C : public virtual A 7 { 8 ... 9 };
这是程序的输出就会变成:
可见这个时候类D的实例d就只有一个类A实例。
二义性
请看下边程序:
1 #include <iostream> 2 using namespace std; 3 4 class A 5 { 6 private: 7 char idA; 8 9 public: 10 A(){ 11 idA = 'A'; 12 cout << "Constructor of A is called!" << endl; 13 } 14 ~A() { cout << "Destructor of A is called!" << endl; } 15 char getID() { return idA; } 16 }; 17 18 class B : public virtual A 19 { 20 private: 21 char idB; 22 23 public: 24 B(){ 25 idB = 'B'; 26 cout << "Constructor of B is called!" << endl; 27 } 28 ~B() { cout << "Destructor of B is called!" << endl; } 29 char getID() { return idB; } 30 }; 31 32 class C : public virtual A 33 { 34 private: 35 char idC; 36 37 public: 38 C(){ 39 idC = 'C'; 40 cout << "Constructor of C is called!" << endl; 41 } 42 ~C() { cout << "Destructor of C is called!" << endl; } 43 char getID() { return idC; } 44 }; 45 46 class D : public B, public C 47 { 48 private: 49 char idD; 50 51 public: 52 D(){ 53 idD = 'D'; 54 cout << "Constructor of D is called!" << endl; 55 } 56 ~D() { cout << "Destructor of D is called!" << endl; } 57 // char getID() { return idD; } 58 }; 59 60 int main() 61 { 62 D d; 63 cout << d.getID() << endl; 64 65 return 0; 66 }
在main函数中,第63行的d.getID()会优先在类D中查找有没有getID()的定义,如果没有就会到其父类查找;而恰好其父类B、C(同级)均定义了相同的getID()(类A的getID()定义存不存在都没关系),这时d.getID()就不知道要调用B类中的getID()还是C类中的,从而导致二义性。
不过我们可以通过d.B::getID()、d.C::getID()来指明具体要调用哪一个类的getID。但我们总不会想到这样子去做,而且这样子做也比较麻烦。
虚函数
对于多重继承的虚函数同样存在二义性。
先看一下程序:
1 #include <iostream> 2 using namespace std; 3 4 class A 5 { 6 private: 7 char idA; 8 9 public: 10 A(){ 11 idA = 'A'; 12 cout << "Constructor of A is called!" << endl; 13 } 14 ~A() { cout << "Destructor of A is called!" << endl; } 15 char getID() { return idA; } 16 }; 17 18 class B : public virtual A 19 { 20 private: 21 char idB; 22 23 public: 24 B(){ 25 idB = 'B'; 26 cout << "Constructor of B is called!" << endl; 27 } 28 ~B() { cout << "Destructor of B is called!" << endl; } 29 char getID() { return idB; } 30 }; 31 32 class C : public virtual A 33 { 34 private: 35 char idC; 36 37 public: 38 C(){ 39 idC = 'C'; 40 cout << "Constructor of C is called!" << endl; 41 } 42 ~C() { cout << "Destructor of C is called!" << endl; } 43 char getID() { return idC; } 44 }; 45 46 class D : public B, public C 47 { 48 private: 49 char idD; 50 51 public: 52 D(){ 53 idD = 'D'; 54 cout << "Constructor of D is called!" << endl; 55 } 56 ~D() { cout << "Destructor of D is called!" << endl; } 57 char getID() { return idD; } 58 }; 59 60 int main() 61 { 62 D d; 63 A a = d; 64 B b = d; 65 C c = d; 66 cout << a.getID() << endl; 67 cout << b.getID() << endl; 68 cout << c.getID() << endl; 69 cout << d.getID() << endl; 70 71 return 0; 72 }
程序输出如下:
上边程序第63~65行相当于a、b、c将d进行了分割(函数是否是虚函数在这里并无关系,而且注意这里的a、b、c、d都不是指针),分割出属于自己的部分,所以调用getID()的时候能正确反映具体的类。
我们再来看一个程序:
1 #include <iostream> 2 using namespace std; 3 4 class A 5 { 6 private: 7 char idA; 8 9 public: 10 A(){ 11 idA = 'A'; 12 cout << "Constructor of A is called!" << endl; 13 } 14 ~A() { cout << "Destructor of A is called!" << endl; } 15 virtual char getID() { return idA; } 16 }; 17 18 class B : public virtual A 19 { 20 private: 21 char idB; 22 23 public: 24 B(){ 25 idB = 'B'; 26 cout << "Constructor of B is called!" << endl; 27 } 28 ~B() { cout << "Destructor of B is called!" << endl; } 29 virtual char getID() { return idB; } 30 }; 31 32 class C : public virtual A 33 { 34 private: 35 char idC; 36 37 public: 38 C(){ 39 idC = 'C'; 40 cout << "Constructor of C is called!" << endl; 41 } 42 ~C() { cout << "Destructor of C is called!" << endl; } 43 virtual char getID() { return idC; } 44 }; 45 46 class D : public B, public C 47 { 48 private: 49 char idD; 50 51 public: 52 D(){ 53 idD = 'D'; 54 cout << "Constructor of D is called!" << endl; 55 } 56 ~D() { cout << "Destructor of D is called!" << endl; } 57 virtual char getID() { return idD; } 58 }; 59 60 int main() 61 { 62 D *d = new D(); 63 A *a = d; 64 B *b = d; 65 C *c = d; 66 cout << a->getID() << endl; 67 cout << b->getID() << endl; 68 cout << c->getID() << endl; 69 cout << d->getID() << endl; 70 71 delete d; 72 return 0; 73 }
程序的输出如下:
从输出结果可以看出,类D的getID覆盖其所有父类的getID。需要注意的是,当我们在类D的一个父类,如A中不设定getID为虚函数,则“A *a = d”的效果仍然跟分割d指向的内存的效果一样。