zoukankan      html  css  js  c++  java
  • 【C++ Primer | 15】虚继承

    虚基类

    一、虚基类介绍

    多继承时很容易产生命名冲突,即使我们很小心地将所有类中的成员变量和成员函数都命名为不同的名字,命名冲突依然有可能发生,比如非常经典的菱形继承层次。如下图所示:

                                                                                             

    类A派生出类B和类C,类D继承自类B和类C,这个时候类A中的成员变量和成员函数继承到类D中变成了两份,一份来自 A-->B-->D 这一路,另一份来自 A-->C-->D 这一条路。

    在一个派生类中保留间接基类的多份同名成员,虽然可以在不同的成员变量中分别存放不同的数据,但大多数情况下这是多余的:因为保留多份成员变量不仅占用较多的存储空间,还容易产生命名冲突,而且很少有这样的需求。

    为了解决这个问题,C++提供了虚基类,使得在派生类中只保留间接基类的一份成员。

    声明虚基类只需要在继承方式前面加上 virtual 关键字,请看下面的例子:

     1 #include <iostream>
     2 using namespace std;
     3 
     4 class A {
     5 protected:
     6     int a;
     7 public:
     8     A(int a) : a(a) {}
     9 };
    10 
    11 class B : virtual public A {
    12 protected:
    13     int b;
    14 public:
    15     B(int a, int b) : A(a), b(b) {}
    16 };
    17 
    18 class C : virtual public A {
    19 protected:
    20     int c;
    21 public:
    22     C(int a, int c) : A(a), c(c) {}
    23 };
    24 
    25 class D : virtual public B, virtual public C {
    26 private:
    27     int d;
    28 public:
    29     D(int a, int b, int c, int d) : A(a), B(a, b), C(a, c), d(d) {}
    30     void display();
    31 };
    32 
    33 void D::display()
    34 {
    35     cout << "a = " << a << endl;
    36     cout << "b = " << b << endl;
    37     cout << "c = " << c << endl;
    38     cout << "d = " << d << endl;
    39 }
    40 
    41 int main()
    42 {
    43     (new D(1, 2, 3, 4))->display();
    44     return 0;
    45 }

    输出结果:

    本例中我们使用了虚基类,在派生类D中只有一份成员变量 a 的拷贝,所以在 display() 函数中可以直接访问 a,而不用加类名和域解析符。

    请注意派生类D的构造函数,与以往的用法有所不同。以往,在派生类的构造函数中只需负责对其直接基类初始化,再由其直接基类负责对间接基类初始化。现在,由于虚基类在派生类中只有一份成员变量,所以对这份成员变量的初始化必须由派生类直接给出。如果不由最后的派生类直接对虚基类初始化,而由虚基类的直接派生类(如类B和类C)对虚基类初始化,就有可能由于在类B和类C的构造函数中对虚基类给出不同的初始化参数而产生矛盾。所以规定:在最后的派生类中不仅要负责对其直接基类进行初始化,还要负责对虚基类初始化。

    有的读者会提出:类D的构造函数通过初始化表调了虚基类的构造函数A,而类B和类C的构造函数也通过初始化表调用了虚基类的构造函数A,这样虚基类的构造函数岂非被调用了3次?大家不必过虑,C++编译系统只执行最后的派生类对虚基类的构造函数的调用,而忽略虚基类的其他派生类(如类B和类C)对虚基类的构造函数的调用,这就保证了虚基类的数据成员不会被多次初始化。

    最后请注意:为了保证虚基类在派生类中只继承一次,应当在该基类的所有直接派生类中声明为虚基类,否则仍然会出现对基类的多次继承。

    参考资料

  • 相关阅读:
    Atitit attilax要工作研究的要素 纪要 方案 趋势 方向 概念 理论
    Atitit 常见每日流程日程日常工作.docx v7 r8f
    Atitit it 互联网 软件牛人的博客列表
    Atitit 信息链(Information Chain)的概念理解 attilax总结
    Atitit 知识点的体系化 框架与方法 如何了解 看待xxx
    Atitit 聚合搜索多个微博 attilax总结
    Atitit 企业知识管理PKM与PIM
    Atitit 项目沟通管理 Atitit 沟通之道 attilax著.docx
    Atitit 项目管理软件 在线服务 attilax总结 1. 项目管理协作的历史 1 1.1. Worktile 406k 1 1.2. Teambition  584k in baidu
    Atitit.每周末总结 于每周一计划日程表 流程表 v8 import 上周遗漏日志补充 检查话费 检查流量情况 Crm问候 Crm表total and 问候
  • 原文地址:https://www.cnblogs.com/sunbines/p/9215687.html
Copyright © 2011-2022 走看看