C++多重继承与虚拟继承

时间:2022-09-01 15:54:08

  本文只是粗浅讨论一下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 }

  上述程序的输出为:

  C++多重继承与虚拟继承

  由上边结果可以看出,析构函数调用次序跟构造函数是相反的。另外,构造函数调用次序跟类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 };

  这是程序的输出就会变成:

  C++多重继承与虚拟继承

  可见这个时候类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 }

  程序输出如下:

  C++多重继承与虚拟继承

  上边程序第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 }

  程序的输出如下:

  C++多重继承与虚拟继承

  从输出结果可以看出,类D的getID覆盖其所有父类的getID。需要注意的是,当我们在类D的一个父类,如A中不设定getID为虚函数,则“A *a = d”的效果仍然跟分割d指向的内存的效果一样。