C++:菱形继承与虚继承

看下面这个示例代码

class A{
public:
int num=10;
A(){cout<<"A构造"<<endl;}
virtual void fun(){cout<<"A虚函数"<<endl;}
};

class B:public A{
public:
B(){cout<<"B构造"<<endl;}
void fun(){cout<<"B虚函数"<<endl;}
};

class C:public A{
public:
C(){cout<<"C构造"<<endl;}
void fun(){cout<<"C虚函数"<<endl;}
};

class D:public C,public B{
public:
D(){cout<<"D构造"<<endl;}
void fun(){cout<<"D虚函数"<<num<<endl;}
};

这段代码你会发现这段代码编译不通过,因为D类的虚函数在访问num时不知道访问哪个,将这个虚函数里面的内容清空然后执行会得到以下结果:
在这里插入图片描述

可以看到D多次继承了同一个基类,这样就造成了构造D时构造了多个A,问题本身没有什么影响但是如果D需要访问A中的东西时就会有两个渠道,通过B或者C,这时候加上作用域的话就可以运行了,但是这样太麻烦了,而且对共同的基类A真的没必要构造两次,为了处理这种情况引入了虚继承的概念。
之前介绍过虚函数,虚继承和它类似

  • 发生虚继承的时候就会产生一个虚基表和相应的虚基表指针
  • 虚基表是静态的,这点和虚函数表差不多
  • 发生虚继承后对任意类只会创建一次
  • 虚基表存储虚基表指针到其基类的偏移量
  • 一个类只会有一个虚基表,但是可以有多个虚基表指针

这些概念后面梳理,现在先看修改后的代码

class A{
public:
int num=10;
A(){cout<<"A构造"<<endl;}
virtual void fun(){cout<<"A虚函数"<<endl;}
};
class B:virtual public A{
public:
B(){cout<<"B构造"<<endl;}
void fun(){cout<<"B虚函数"<<endl;}
};
class C:virtual public A{
public:
C(){cout<<"C构造"<<endl;}
void fun(){cout<<"C虚函数"<<endl;}
};
class D:virtual public C,virtual public B{
public:
D(){cout<<"D构造"<<endl;}
void fun(){cout<<"D虚函数"<<num<<endl;}
};

看看执行结果
在这里插入图片描述
可以看出问题被解决了,接下来看它在内存中的分布
在vs开发者命令行模式下输入下面的指令即可

cl /d1 reportSingleClassLayoutXXX 文件名.cpp

xxx替换为自己的类名,我这里是D

在这里插入图片描述
可以看到类的结构还是很清晰的,每个使用虚继承的类头部都有一个虚基表指针,同时虚函数表指针不再出现在头部,不需要过多解释自己看吧
在这里插入图片描述
虚继承通过减少基类的重复创建解决了多次继承同一基类造成的二义性问题,借助虚基表完成了基类的定位,可以说很是牛皮,但是缺点也很明显它破坏了类中的原有布局,它使类中的内存分布情况程序多样化,现在虚函数表指针可能有多个甚至位置也会按照继承的顺序而改变,内存的分布依赖于代码书写,所以说要慎用,虽然我们不需要过多关注内存,但是对性能的影响却是真实存在的。

  • 19
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值