一般来说,在派生类中对基类成员的访问是应该是唯一的。但是,由于在多继承的情况下,可能出现基类中某个成员的访问不唯一的情况,这称为对基类成员访问的二义性。

在多继承的情况下,通常有两种可能出现的二义性。
1 派生类的多个基类中调用其同名成员时可能出现二义性

class A
{
public:void f(){}
};class B
{
public:void f() {}void g() {}
};class C : public A, public B
{
public:void h() {}
};

如果定义一个类C的对象c, 则对函数f()的访问c.f()就会出现二义性,是访问A中的f,还是B中的f。
可以通过成员名限定发来消除二义性。例如:c.A::f() 或者 c.B::f()。

2 派生类有共同基类时访问公共基类成员可能出现二义性

class A
{
public:int a;
};class B1 : public A
{
public:int b1;
};class B2 : public A
{
public:int b2;
};class C : public B1, public B2
{
public:int c;
};

如果定义了一个类C的对象c,则c.a 或者 c.A::a 都有问题。
正确的访问为c.B1::a 或者 c.B2::a。

对于第2中二义性,基类A被保存了2份。对C进行初始化时,类A会被初始化两次。 为了彻底避免在这种结构中的二义性,在创建派生类对象时对公共基类只进行一次初始化,则引入了虚继承和虚基类。

声明和定义:
虚继承的引入是为了解决二义性问题。其说明格式如下:
class <类名> : virtual <继承方式> <基类名>
其中,virtual是虚继承的关键字。
以虚继承方式被继承的基类称为虚基类。
经过这样的声明后,当基类通过多条派生路径被一个派生类继承时,该派生类只继承该基类一次。

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

例如:

class A
{
public:int a;
};class B1 : virtual public A
{
public:int b1;
};class B2 : virtual public A
{
public:int b2;
};class C : public B1, public B2
{
public:int c;
};

如果定义了类C的对象c,则c.a是正确的。并且,类C中只有一份类A的内容。

含有虚基类的子类的内存结构:
还以上面的代码为例,类C是如何实现,只包含一份类A的呢?在引入虚基类之后,虚基类的直接子类中就包含了一个指向虚基类的指针,这个指针称为虚基类指针。类B1和B2中各有一个指向类A的虚基类指针,类C中只继承了一份类A,但继承了类B1和B2中的指向类A的指针。

含虚基类的派生类的构造函数
为了初始化基类的子对象,派生类的构造函数要调用基类的构造函数。对于虚基类,由于派生类的对象中只有一个虚基类的子对象。为了保证虚基类子对象只被调用一次,这个虚基类构造函数必须只被调用一次。由于继承机构可能很深,规定将在建立对象时指定的类称为最派生类。C++语言规定,虚基类子对象是由最派生类的构造函数通过调用虚基类的构造函数进行初始化的。 而该派生类的直接基类中所列出的对这个虚基类的构造函数的调用在执行时被忽略,这样保证了对虚基类的子对象只初始化一次。

C++语言规定,在一个成员初始化列表中,对虚基类的初始化先于非虚基类的初始化。

class A
{
public:A(int a) : a(a) {}int a;
};class B1 :  virtual public A
{
public:B1(int a,int b) : A(a), b1(b) {}int b1;
};class B2 :  virtual public A
{
public:B2(int a, int b) : A(a), b2(b) {}int b2;
};class C : public B1, public B2
{
public:C(int a, int b1,int b2, int c) : A(a), B1(b1,b1), B2(b2,b2), c(c) {}int c;
};int main()
{B1 b1(2,2);cout<<b1.a<<endl;B2 b2(3,3);cout<<b2.a<<endl;C c(1,2,3,4);cout<<c.a<<endl;
}

结果: 2 3 1
在这段代码中,在类C中直接对类A进行初始化。并且,虽然类B1和类B2中都有对类A的初始化,但在对类C进行初始化时,B1和B2中对类A的初始化都被忽略了。这样就保证了类A只被初始化一次。

多继承的二义性和虚继承(虚基类)相关推荐

  1. [C++] - 纯虚函数 抽象基类 接口类

    翻译自:https://www.learncpp.com/cpp-tutorial/126-pure-virtual-functions-abstract-base-classes-and-inter ...

  2. C++ 虚函数在基类与派生类对象间的表现及其分析

    近来看了侯捷的<深入浅出MFC>,读到C++重要性质中的虚函数与多态那部分内容时,顿时有了疑惑.因为书中说了这么一句:使用"基类之指针"指向"派生类之对象&q ...

  3. Cpp 对象模型探索 / 虚继承带虚函数的基类的子类的内存布局

    源码 class Base { public:Base() {}virtual void func() {}int bi_; };class Son:virtual public Base { pub ...

  4. C++_虚继承_虚函数_纯虚函数(多继承的二义性,多态)

    基本信息 每一个类都有一个虚表,以及虚表指针; 虚表的内容是编译器决定的,虚表中用于存放虚函数的指针, 程序运行时的类型信息等; 每个多态对象都存放着一个指向当前类型的虚表的指针, 该指针在构造函数中 ...

  5. C++学习11:C++多继承及二义性解决方案:虚继承

    前面提到的继承方式都是单继承,即派生类的基类只有一个.但是在实际开发应用中,一个派生类往往会有多个基类,派生类从多个基类中获取所需要的属性,这种继承方式称为多继承.例如超人狗,既具有超人的特性,能在天 ...

  6. 虚继承c语言例子,C/C++ 多继承{虚基类,虚继承,构造顺序,析构顺序}

    C/C++:一个基类继承和多个基类继承的区别 1.对多个基类继承会出现类之间嵌套时出现的同名问题,如果同名变量或者函数出现不在同一层次,则底层派生隐藏外层比如继承基类的同名变量和函数,不会出现二义性, ...

  7. 多重继承与虚继承编程实验

    多重继承与虚继承编程实验 基本知识 多重继承 多重继承下的类作用域 虚继承 构造函数与虚继承 关于本程序 示例代码 Animal_virtual_baseVers.h virt-inherit.cpp ...

  8. C++虚继承(五) --- 虚拟继承的概念

    C++中虚拟继承的概念 为了解决从不同途径继承来的同名的数据成员在内存中有不同的拷贝造成数据不一致问题,将共同基类设置为虚基类.这时从不同的路径继承过来的同名数据成员在内存中就只有一个拷贝,同一个函数 ...

  9. c++ map 析构函数_C++|类继承关系中的虚函数、虚析构函数、虚基类

    在继承关系中,虚函数.虚析构函数.虚基类中使用的关键字virtual都是在告诉编译器,此处要进行特殊处理: 虚函数:函数重写时的要求编译器动态绑定来实现多多态 : 虚析构函数:当基类指针指向在堆内实现 ...

最新文章

  1. java 集合 介绍_java集合类基本简介
  2. oracle flex cluster,Oracle Flex ASM和Flex集群
  3. python爬虫 小白轻松从0到1_小白学 Python 爬虫(1):开篇
  4. Java黑皮书课后题第7章:**7.3(计算数字的出现次数)编写程序,读取1到100之间的整数,然后计算每个数出现的次数。假定输入0表示结束
  5. 网络作者的心声-23、网络作家大多赚'辛苦钱'
  6. 从头开始学一个android activity
  7. Linux问题处理————命令提示符显示 bash-4.1# 解决方案
  8. ubuntu 自动清理/tmp目录
  9. android 动态创建view,react-native动态创建Android View 无效果
  10. 软件是怎么控制硬件的?
  11. 2012-3-29之前的微博
  12. 上下文无关文法(例题+计算)
  13. strtok()函数详解!
  14. 2022 东北四省赛 VP记录/补题
  15. 标题栏、菜单栏、工具栏、状态栏
  16. 满减活动基础算法-java-类似淘宝满200减30
  17. python批量telnet检测IP地址的端口是否开放
  18. python时间戳转换成时间_Python时间,日期,时间戳之间转换,时间转换时间戳,Python时间戳转换时间,Python时间转换时间戳...
  19. BioPython ② | 面向对象编程Object Oriented Programming
  20. 王半仙儿的日记-0003

热门文章

  1. 我的周刊可以在线访问了
  2. 用java模拟dnf武器强化的过程
  3. 中值联认证中心发布的区块链证书验证真伪的办法
  4. IDEA中设置JVM参数
  5. 微信公众号之创建自定义菜单
  6. 取消苹果商店简短验证_苹果12怎么下载软件 苹果12下载软件方法
  7. 接口自动化覆盖率统计——Jacoco使用
  8. 红底证件照背景怎么弄?试试这几种方法非常简单
  9. 【书屋】JavaScript高级程序设计
  10. Unity中使用粒子特效(Particle System)制作烟花和烛光效果