虚继承(Virtual Inheritance)
为了解决多继承时的命名冲突和冗余数据问题,C++ 提出了虚继承,使得在派生类中只保留一份间接基类的成员。

在继承方式前面加上 virtual 关键字就是虚继承,请看下面的例子:

//间接基类A
class A{protected:int m_a;
};
//直接基类B
class B: virtual public A{  //虚继承
protected:int m_b;
};
//直接基类C
class C: virtual public A{  //虚继承
protected:int m_c;
};
//派生类D
class D: public B, public C{public:void seta(int a){ m_a = a; }  //正确void setb(int b){ m_b = b; }  //正确void setc(int c){ m_c = c; }  //正确void setd(int d){ m_d = d; }  //正确
private:int m_d;
};
int main(){D d;return 0;
}

这段代码使用虚继承重新实现了上图所示的菱形继承,这样在派生类 D 中就只保留了一份成员变量 m_a,直接访问就不会再有歧义了。
虚继承的目的是让某个类做出声明,承诺愿意共享它的基类。其中,这个被共享的基类就称为虚基类(Virtual Base Class),本例中的 A 就是一个虚基类。在这种机制下,不论虚基类在继承体系中出现了多少次,在派生类中都只包含一份虚基类的成员。
现在让我们重新梳理一下本例的继承关系,如下图所示:


观察这个新的继承体系,我们会发现虚继承的一个不太直观的特征:必须在虚派生的真实需求出现前就已经完成虚派生的操作。在上图中,当定义 D 类时才出现了对虚派生的需求,但是如果 B 类和 C 类不是从 A 类虚派生得到的,那么 D 类还是会保留 A 类的两份成员。

换个角度讲,虚派生只影响从指定了虚基类的派生类中进一步派生出来的类,它不会影响派生类本身。

在实际开发中,位于中间层次的基类将其继承声明为虚继承一般不会带来什么问题。通常情况下,使用虚继承的类层次是由一个人或者一个项目组一次性设计完成的。对于一个独立开发的类来说,很少需要基类中的某一个类是虚基类,况且新类的开发者也无法改变已经存在的类体系。

C++标准库中的 iostream 类就是一个虚继承的实际应用案例。iostream 从 istream 和 ostream 直接继承而来,而 istream 和 ostream 又都继承自一个共同的名为 base_ios 的类,是典型的菱形继承。此时 istream 和 ostream 必须采用虚继承,否则将导致 iostream 类中保留两份 base_ios 类的成员。


虚基类成员的可见性
因为在虚继承的最终派生类中只保留了一份虚基类的成员,所以该成员可以被直接访问,不会产生二义性。此外,如果虚基类的成员只被一条派生路径覆盖,那么仍然可以直接访问这个被覆盖的成员。但是如果该成员被两条或多条路径覆盖了,那就不能直接访问了,此时必须指明该成员属于哪个类。

以图2中的菱形继承为例,假设 A 定义了一个名为 x 的成员变量,当我们在 D 中直接访问 x 时,会有三种可能性:
如果 B 和 C 中都没有 x 的定义,那么 x 将被解析为 A 的成员,此时不存在二义性。
如果 B 或 C 其中的一个类定义了 x,也不会有二义性,派生类的 x 比虚基类的 x 优先级更高。
如果 B 和 C 中都定义了 x,那么直接访问 x 将产生二义性问题。

可以看到,使用多继承经常会出现二义性问题,必须十分小心。上面的例子是简单的,如果继承的层次再多一些,关系更复杂一些,程序员就很容易陷人迷魂阵,程序的编写、调试和维护工作都会变得更加困难,因此我不提倡在程序中使用多继承,只有在比较简单和不易出现二义性的情况或实在必要时才使用多继承,能用单一继承解决的问题就不要使用多继承。也正是由于这个原因,C++ 之后的很多面向对象的编程语言,例如 Java、C#、PHP 等,都不支持多继承。

C++虚继承和虚基类详解(二)相关推荐

  1. SpringSecurity权限管理框架系列(六)-Spring Security框架自定义配置类详解(二)之authorizeRequests配置详解

    1.预置演示环境 这个演示环境继续沿用 SpringSecurit权限管理框架系列(五)-Spring Security框架自定义配置类详解(一)之formLogin配置详解的环境. 2.自定义配置类 ...

  2. C++虚继承和虚基类详解(一)

    多继承(Multiple Inheritance)是指从多个直接基类中产生派生类的能力,多继承的派生类继承了所有父类的成员.尽管概念上非常简单,但是多个基类的相互交织可能会带来错综复杂的设计问题,命名 ...

  3. Dedesql数据库类详解(二次开发必备教程)(转)

    http://www.dedecms.com/help/development/2009/1028/1076.html 织梦DedeCMS的二次开发不仅仅是会写写织梦的标签,会制作织梦的模板.很多时候 ...

  4. dede mysql数据库_Dedesql数据库类详解(二次开发必备教程)

    注意:图片为缩略图,点击看大图.更清晰... 今天花点时间讲解下织梦的sql数据库类,近期本来是准备录制一套视频教程的,但由于视频压缩的问题迟迟没有开展工作,如果大家有什么好的视频压缩方式可以通过邮件 ...

  5. mysql数据库 二次开发_Dedesql数据库类详解(二次开发必备教程)

    -- ---------------------------- -- 表 `dede_test`结构 -- ---------------------------- DROP TABLE IF EXI ...

  6. C++虚继承和虚析构函数

    虚继承 当一个基类被声明为虚基类后,即使它成为了多继承链路上的公共基类,最后的派生类中也只有它的一个备份.例如: class CBase { }: class CDerive1:virtual pub ...

  7. C++中的虚继承与虚基类

    1.Cpp中的虚继承与虚基类 在多继承时,很容易产生命名冲突的问题,即使我们很小心地将所有类中的成员变量和成员函数都命名为不同的名字,命名冲突依然有可能发生,比如典型的是菱形继承,如下图所示: 类A派 ...

  8. 多重继承、虚继承与虚基类

    一.多重继承 单重继承--一个派生类最多只能有一个基类 多重继承--一个派生类可以有多个基类 class 类名: 继承方式 基类1,继承方式 基类2,-. {-.}; 派生类同时继承多个基类的成员,更 ...

  9. C++中的各种“虚“-- 虚函数、纯虚函数、虚继承、虚基类、虚析构、纯虚析构、抽象类讲解

    C++中的各种"虚" 1. 菱形继承 1.1 虚继承 && 虚基类 1.2 虚基类指针(vbptr)&& 虚基类表(vbtable) 2. 多态 2 ...

最新文章

  1. Python爬虫利器之Beautiful Soup的全世界最强用法 五百行文章!
  2. 彻底搞懂 Git-Rebase
  3. 禁用Intel ME
  4. Linux中的报错命令,Linux学习教程-Linux下命令的一些异常情况
  5. jpa 实体到数据库
  6. DoIP(二)——报文类型
  7. fedora 19 安装中文语言包
  8. [Android-ARCore开发]ARCore从入门到放弃1-Demo上手
  9. 本周(12.23-12.29)半价电子书
  10. 利用Vue制作一个简单的走马灯
  11. 家里蹲太孤单?来用python画一只单身狗吧
  12. Cobbler自动部署CentOS系统
  13. 达人评测华为MatePadPro2怎么样
  14. 京东营销案例与运营直播手册(共47份)
  15. 计算机文化基础知识在未来工作中的应用论文,计算机文化基础论文
  16. php解析mht,php解析mht文件转换成html的实例
  17. 嵌入式软件工程师面试题(九)
  18. opecv BGR转NV21
  19. VOC2012数据集百度云链接
  20. 美国程序员枪击案后的48小时,请放过程序员吧!

热门文章

  1. 数字锁相环的matlab仿真
  2. 基于matlab的pointnet++深度学习网络点云数据分类
  3. MacOS使用Charles抓去HTTPS数据
  4. vim末行模式下相关操作+配置文件
  5. 用java将excel数据导入txt
  6. C 语言指针 5 分钟教程
  7. [01分数规划]【学习笔记】
  8. 8.tomcat认证访问
  9. Windows 10的应用体验之二
  10. 日常总结:自学操作系统基础的一些领悟