虚基类和虚继承


以一
以一 2023-12-31 04:02:29 64364
分类专栏: 资讯

在虚继承中,虚基类是由最终的派生类初始化的,换句话说,最终派生类的构造函数必须要调用虚基类的构造函数。对最终的派生类来说,虚基类是间接基类,而不是直接基类。这跟普通继承不同,在普通继承中,派生类构造函数中只能调用直接基类的构造函数,不能调用间接基类的。

下面我们以菱形继承为例来演示构造函数的调用:

  1. #include <iostream>
  2. using namespace std;
  3.  
  4. //虚基类A
  5. class A{
  6. public:
  7. A(int a);
  8. protected:
  9. int m_a;
  10. };
  11. A::A(int a): m_a(a){ }
  12.  
  13. //直接派生类B
  14. class B: virtual public A{
  15. public:
  16. B(int a, int b);
  17. public:
  18. void display();
  19. protected:
  20. int m_b;
  21. };
  22. B::B(int a, int b): A(a), m_b(b){ }
  23. void B::display(){
  24. cout<<"m_a="<<m_a<<", m_b="<<m_b<<endl;
  25. }
  26.  
  27. //直接派生类C
  28. class C: virtual public A{
  29. public:
  30. C(int a, int c);
  31. public:
  32. void display();
  33. protected:
  34. int m_c;
  35. };
  36. C::C(int a, int c): A(a), m_c(c){ }
  37. void C::display(){
  38. cout<<"m_a="<<m_a<<", m_c="<<m_c<<endl;
  39. }
  40.  
  41. //间接派生类D
  42. class D: public B, public C{
  43. public:
  44. D(int a, int b, int c, int d);
  45. public:
  46. void display();
  47. private:
  48. int m_d;
  49. };
  50. D::D(int a, int b, int c, int d): A(a), B(90, b), C(100, c), m_d(d){ }
  51. void D::display(){
  52. cout<<"m_a="<<m_a<<", m_b="<<m_b<<", m_c="<<m_c<<", m_d="<<m_d<<endl;
  53. }
  54.  
  55. int main(){
  56. B b(10, 20);
  57. b.display();
  58.  
  59. C c(30, 40);
  60. c.display();
  61.  
  62. D d(50, 60, 70, 80);
  63. d.display();
  64. return 0;
  65. }

运行结果:
m_a=10, m_b=20
m_a=30, m_c=40
m_a=50, m_b=60, m_c=70, m_d=80

请注意第 50 行代码,在最终派生类 D 的构造函数中,除了调用 B 和 C 的构造函数,还调用了 A 的构造函数,这说明 D 不但要负责初始化直接基类 B 和 C,还要负责初始化间接基类 A。而在以往的普通继承中,派生类的构造函数只负责初始化它的直接基类,再由直接基类的构造函数初始化间接基类,用户尝试调用间接基类的构造函数将导致错误。

现在采用了虚继承,虚基类 A 在最终派生类 D 中只保留了一份成员变量 m_a,如果由 B 和 C 初始化 m_a,那么 B 和 C 在调用 A 的构造函数时很有可能给出不同的实参,这个时候编译器就会犯迷糊,不知道使用哪个实参初始化 m_a。

为了避免出现这种矛盾的情况,C++ 干脆规定必须由最终的派生类 D 来初始化虚基类 A,直接派生类 B 和 C 对 A 的构造函数的调用是无效的。在第 50 行代码中,调用 B 的构造函数时试图将 m_a 初始化为 90,调用 C 的构造函数时试图将 m_a 初始化为 100,但是输出结果有力地证明了这些都是无效的,m_a 最终被初始化为 50,这正是在 D 中直接调用 A 的构造函数的结果。

另外需要关注的是构造函数的执行顺序。虚继承时构造函数的执行顺序与普通继承时不同:在最终派生类的构造函数调用列表中,不管各个构造函数出现的顺序如何,编译器总是先调用虚基类的构造函数,再按照出现的顺序调用其他的构造函数;而对于普通继承,就是按照构造函数出现的顺序依次调用的。

修改本例中第 50 行代码,改变构造函数出现的顺序:

  1. D::D(int a, int b, int c, int d): B(90, b), C(100, c), A(a), m_d(d){ }

虽然我们将 A() 放在了最后,但是编译器仍然会先调用 A(),然后再调用 B()、C(),因为 A() 是虚基类的构造函数,比其他构造函数优先级高。如果没有使用虚继承的话,那么编译器将按照出现的顺序依次调用 B()、C()、A()。

网站声明:如果转载,请联系本站管理员。否则一切后果自行承担。

本文链接:https://www.xckfsq.com/news/show.html?id=33192
赞同 0
评论 0 条
以一L0
粉丝 0 发表 893 + 关注 私信
上周热门
银河麒麟添加网络打印机时,出现“client-error-not-possible”错误提示  1487
银河麒麟打印带有图像的文档时出错  1405
银河麒麟添加打印机时,出现“server-error-internal-error”  1194
统信操作系统各版本介绍  1116
统信桌面专业版【如何查询系统安装时间】  1114
统信桌面专业版【全盘安装UOS系统】介绍  1069
麒麟系统也能完整体验微信啦!  1026
统信【启动盘制作工具】使用介绍  672
统信桌面专业版【一个U盘做多个系统启动盘】的方法  616
信刻全自动档案蓝光光盘检测一体机  526
本周热议
我的信创开放社区兼职赚钱历程 40
今天你签到了吗? 27
信创开放社区邀请他人注册的具体步骤如下 15
如何玩转信创开放社区—从小白进阶到专家 15
方德桌面操作系统 14
我有15积分有什么用? 13
用抖音玩法闯信创开放社区——用平台宣传企业产品服务 13
如何让你先人一步获得悬赏问题信息?(创作者必看) 12
2024中国信创产业发展大会暨中国信息科技创新与应用博览会 9
中央国家机关政府采购中心:应当将CPU、操作系统符合安全可靠测评要求纳入采购需求 8

添加我为好友,拉您入交流群!

请使用微信扫一扫!