在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
多重继承下面,再让我们来看看多重继承中的情况,假设有下面这样一个类的继承关系。 注意:子类只overwrite了父类的f()函数,而还有一个是自己的函数(我们这样做的目的是为了用g1()作为一个标记来标明子类的虚函数表)。而且每个类中都有一个自己的成员变量: 们的类继承的源代码如下所示:父类的成员初始为10,20,30,子类的为100 1 #include<iostream> 2 using namespace std; 3 4 class Base1 { 5 public: 6 int ibase1; 7 Base1() :ibase1(10) {} 8 virtual void f() { cout << "Base1::f()" << endl; } 9 virtual void g() { cout << "Base1::g()" << endl; } 10 virtual void h() { cout << "Base1::h()" << endl; } 11 12 }; 13 14 class Base2 { 15 public: 16 int ibase2; 17 Base2() :ibase2(20) {} 18 virtual void f() { cout << "Base2::f()" << endl; } 19 virtual void g() { cout << "Base2::g()" << endl; } 20 virtual void h() { cout << "Base2::h()" << endl; } 21 }; 22 23 class Base3 { 24 public: 25 int ibase3; 26 Base3() :ibase3(30) {} 27 virtual void f() { cout << "Base3::f()" << endl; } 28 virtual void g() { cout << "Base3::g()" << endl; } 29 virtual void h() { cout << "Base3::h()" << endl; } 30 }; 31 32 class Derive : public Base1, public Base2, public Base3 { 33 public: 34 int iderive; 35 Derive() :iderive(100) {} 36 virtual void f() { cout << "Derive::f()" << endl; } 37 virtual void g1() { cout << "Derive::g1()" << endl; } 38 }; 39 40 int main() 41 { 42 typedef void(*Fun)(void); 43 44 Derive d; 45 46 int** pVtab = (int**)&d; 47 48 cout << "[0] Base1::_vptr->" << endl; 49 Fun pFun = (Fun)pVtab[0][0]; 50 cout << " [0] "; 51 pFun(); 52 53 pFun = (Fun)pVtab[0][1]; 54 cout << " [1] "; pFun(); 55 56 pFun = (Fun)pVtab[0][2]; 57 cout << " [2] "; pFun(); 58 59 pFun = (Fun)pVtab[0][3]; 60 cout << " [3] "; pFun(); 61 62 pFun = (Fun)pVtab[0][4]; 63 cout << " [4] "; cout << pFun << endl; 64 65 cout << "[1] Base1.ibase1 = " << (int)pVtab[1] << endl; 66 67 int s = sizeof(Base1) / 4; 68 69 cout << "[" << s << "] Base2::_vptr->" << endl; 70 pFun = (Fun)pVtab[s][0]; 71 cout << " [0] "; pFun(); 72 73 pFun = (Fun)pVtab[s][1]; 74 cout << " [1] "; pFun(); 75 76 pFun = (Fun)pVtab[s][2]; 77 cout << " [2] "; pFun(); 78 79 pFun = (Fun)pVtab[s][3]; 80 cout << " [3] "; 81 cout << pFun << endl; 82 83 cout << "[" << s + 1 << "] Base2.ibase2 = " << (int)pVtab[s + 1] << endl; 84 85 s = s + sizeof(Base2) / 4; 86 87 cout << "[" << s << "] Base3::_vptr->" << endl; 88 pFun = (Fun)pVtab[s][0]; 89 cout << " [0] "; pFun(); 90 91 pFun = (Fun)pVtab[s][1]; 92 cout << " [1] "; pFun(); 93 94 pFun = (Fun)pVtab[s][2]; 95 cout << " [2] "; pFun(); 96 97 pFun = (Fun)pVtab[s][3]; 98 cout << " [3] "; 99 cout << pFun << endl; 100 101 s++; 102 cout << "[" << s << "] Base3.ibase3 = " << (int)pVtab[s] << endl; 103 s++; 104 cout << "[" << s << "] Derive.iderive = " << (int)pVtab[s] << endl; 105 } 输出结果:
使用图片表示是下面这个样子: 我们可以看到:
重复继承面我们再来看看,发生重复继承的情况。所谓重复继承,也就是某个基类被间接地重复继承了多次。 下图是一个继承图,我们重载了父类的f()函数。 其类继承的源代码如下所示。其中,每个类都有两个变量,一个是整形(4字节),一个是字符(1字节),而且还有自己的虚函数,自己overwrite父类的虚函数。如子类D中,f()覆盖了超类的函数, f1() 和f2() 覆盖了其父类的虚函数,Df()为自己的虚函数。 1 #include<iostream> 2 using namespace std; 3 4 class B 5 { 6 public: 7 int ib; 8 char cb; 9 public: 10 B() :ib(0), cb('B') {} 11 12 virtual void f() { cout << "B::f()" << endl; } 13 virtual void Bf() { cout << "B::Bf()" << endl; } 14 }; 15 class B1 : public B 16 { 17 public: 18 int ib1; 19 char cb1; 20 public: 21 B1() :ib1(11), cb1('1') {} 22 23 virtual void f() { cout << "B1::f()" << endl; } 24 virtual void f1() { cout << "B1::f1()" << endl; } 25 virtual void Bf1() { cout << "B1::Bf1()" << endl; } 26 27 }; 28 class B2 : public B 29 { 30 public: 31 int ib2; 32 char cb2; 33 public: 34 B2() :ib2(12), cb2('2') {} 35 36 virtual void f() { cout << "B2::f()" << endl; } 37 virtual void f2() { cout << "B2::f2()" << endl; } 38 virtual void Bf2() { cout << "B2::Bf2()" << endl; } 39 40 }; 41 42 class D : public B1, public B2 43 { 44 public: 45 int id; 46 char cd; 47 public: 48 D() :id(100), cd('D') {} 49 50 virtual void f() { cout << "D::f()" << endl; } 51 virtual void f1() { cout << "D::f1()" << endl; } 52 virtual void f2() { cout << "D::f2()" << endl; } 53 virtual void Df() { cout << "D::Df()" << endl; } 54 55 }; 56 57 int main() 58 { 59 typedef void(*Fun)(void); 60 int** pVtab = NULL; 61 Fun pFun = NULL; 62 63 D d; 64 pVtab = (int**)&d; 65 cout << "[0] D::B1::_vptr->" << endl; 66 pFun = (Fun)pVtab[0][0]; 67 cout << " [0] "; pFun(); 68 pFun = (Fun)pVtab[0][1]; 69 cout << " [1] "; pFun(); 70 pFun = (Fun)pVtab[0][2]; 71 cout << " [2] "; pFun(); 72 pFun = (Fun)pVtab[0][3]; 73 cout << " [3] "; pFun(); 74 pFun = (Fun)pVtab[0][4]; 75 cout << " [4] "; pFun(); 76 pFun = (Fun)pVtab[0][5]; 77 cout << " [5] 0x" << pFun << endl; 78 79 cout << "[1] B::ib = " << (int)pVtab[1] << endl; 80 cout << "[2] B::cb = " << (char)pVtab[2] << endl; 81 cout << "[3] B1::ib1 = " << (int)pVtab[3] << endl; 82 cout << "[4] B1::cb1 = " << (char)pVtab[4] << endl; 83 84 cout << "[5] D::B2::_vptr->" << endl; 85 pFun = (Fun)pVtab[5][0]; 86 cout << " [0] "; pFun(); 87 pFun = (Fun)pVtab[5][1]; 88 cout << " [1] "; pFun(); 89 pFun = (Fun)pVtab[5][2]; 90 cout << " [2] "; pFun(); 91 pFun = (Fun)pVtab[5][3]; 92 cout << " [3] "; pFun(); 93 pFun = (Fun)pVtab[5][4]; 94 cout << " [4] 0x" << pFun << endl; 95 96 cout << "[6] B::ib = " << (int)pVtab[6] << endl; 97 cout << "[7] B::cb = " << (char)pVtab[7] << endl; 98 cout << "[8] B2::ib2 = " << (int)pVtab[8] << endl; 99 cout << "[9] B2::cb2 = " << (char)pVtab[9] << endl; 100 101 cout << "[10] D::id = " << (int)pVtab[10] << endl; 102 cout << "[11] D::cd = " << (char)pVtab[11] << endl; 103 }
输出结果: 下面是对于子类实例中的虚函数表的图:(第一份图为原作者的图,第二幅图为修改的图) 我们可以看见,最顶端的父类B其成员变量存在于B1和B2中,并被D给继承下去了。而在D中,其有B1和B2的实例,于是B的成员在D的实例中存在两份,一份是B1继承而来的,另一份是B2继承而来的。所以,如果我们使用以下语句,则会产生二义性编译错误: D d; d.ib = 0; //二义性错误 d.B1::ib = 1; //正确 d.B2::ib = 2; //正确 注意,上面例程中的最后两条语句存取的是两个变量。虽然我们消除了二义性的编译错误,但B类在D中还是有两个实例,这种继承造成了数据的重复,我们叫这种继承为重复继承。重复的基类数据成员可能并不是我们想要的。所以,C++引入了虚基类的概念。
钻石型多重虚继承1. 虚继承 1 class B 2 { 3 public: 4 int _b; 5 }; 6 class C1 :virtual public B 7 { 8 public: 9 int _c1; 10 }; 11 class C2 :virtual public B 12 { 13 public: 14 int _c2; 15 }; 16 class D :public C1, public C2 17 { 18 public: 19 int _d; 20 }; 21 22 int main() 23 { 24 D d; 25 d._d = 4; 26 return 0; 27 } 内存布局:先是C1类中的成员,再是C2类中的成员,最后是D类自己的成员,如下图:
2. 虚函数 虚拟继承的出现就是为了解决重复继承中多个间接父类的问题的。钻石型的结构是其最经典的结构。也是我们在这里要讨论的结构: 上述的“重复继承”只需要把B1和B2继承B的语法中加上virtual 关键,就成了虚拟继承,其继承图如下所示: 上图和前面的“重复继承”中的类的内部数据和接口都是完全一样的,只是我们采用了虚拟继承,其省略后的源码如下所示: 1 class B {……}; 2 class B1 : virtual public B{……}; 3 class B2: virtual public B{……}; 4 class D : public B1, public B2{ …… };
在看菱形虚拟继承之前,我们先看一下简单的虚拟单继承是怎么样的,这样便于我们理解复杂一点的菱形虚拟继承,我们先看一组代码: 1 class A { 2 public: 3 int _a; 4 virtual void fun1() {} 5 }; 6 7 class B : public virtual A { 8 public: 9 int _b; 10 //virtual void fun1() {} 11 //virtual void fun2() {} 12 }; 13 14 15 int main() 16 { 17 B b; 18 b._a = 2; 19 b._b = 1; 20 cout << sizeof(A) << endl; 21 cout << sizeof(B) << endl; 22 getchar(); 23 return 0; 24 } 在VS2013的测试结果为8和16,我们试着去掉 //virtual void fun1() {}的注释,也就是 1 class B : public virtual A { 2 public: 3 int _b; 4 virtual void fun1() {} 5 //virtual void fun2() {} 6 }; 此时测试结果仍为8和16,但是当我们去掉//virtual void fun2() {}的注释,也就是 1 class B : public virtual A { 2 public: 3 int _b; 4 virtual void fun1() {} 5 virtual void fun2() {} 6 }; 测试结果为sizeof(A) = 8,sizeof(B) = 20。这是为什么?为了解决这个问题,我们有必要看看在这几种情况下的B对象模型,A类对象模型比较简单,我们知道虚函数必有一个指向虚表的指针,再加上A类对象本身有个int型数据加起来就是8。而对于B对象模型,我们可以简单分几种情况: 唯一的区别就是基类A的虚表指针指向的虚表有没有被重写而已,因此在第一种和第二种情况下,sizeof(B) = 16。 而对于有新增虚函数这种情况,对于B的对象模型则是这样的: 因为有重写基类的虚函数了,所以子类需要额外加一个虚表指针,这样sizeof(B) =20就不难理解了。有了这些知识,我们再看菱形虚拟继承就容易多了,首先对于菱形虚拟继承,它的继承层次图大概像下面这个样子:
为了便于分析,我们可以把这个图拆解下来,也就是说从B到B1,B2是两个单一的虚拟继承,而从B1,B2到则是多继承,这样一来,问题就变得简单多了。对于B到B1,B2两个单一的虚拟继承,根据前面讲的很容易得到B1,B2的对象模型: 接下来就是多继承,这样终于得到了我们D d的对象模型了:
3. 最后再看几道有关的虚继承的题目
对这四种情况分别求sizeof(a), sizeof(b)。结果是什么样的呢?我在VS2013的win32平台测试结果为:
参考资料 |
2023-10-27
2022-08-15
2022-08-17
2022-09-23
2022-08-13
请发表评论