C++虚继承初识
struct Employee { ... };
struct Manager : Employee { ... };
struct Worker : Employee { ... };
struct MiddleManager : Manager, Worker { ... };
如果经理类和工人类都继承自雇员类,很自然地,它们每个类都会从雇员类获得一份数据拷贝。如果不作特殊处理,一线经理类的实例将含有两个雇员类实例,它们分别来自两个雇员基类。如果雇员类成员变量不多,问题不严重;如果成员变量众多,则那份多余的拷贝将造成实例生成时的严重开销。更糟的是,这两份不同的雇员实例可能分别被修改,造成数据的不一致。因此,我们需要让经理类和工人类进行特殊的声明,说明它们愿意共享一份雇员基类实例数据。
很不幸,在C++中,这种“共享继承”被称为“虚继承”,把问题搞得似乎很抽象。虚继承的语法很简单,在指定基类时加上virtual关键字即可。
struct Employee { ... };
struct Manager : virtual Employee { ... };
struct Worker : virtual Employee { ... };
struct MiddleManager : Manager, Worker { ... };
使用虚继承,比起单继承和多重继承有更大的实现开销、调用开销。回忆一下,在单继承和多重继承的情况下,内嵌的基类实例地址比起派生类实例地址来,要么地址相同(单继承,以及多重继承的最靠左基类),要么地址相差一个固定偏移量(多重继承的非最靠左基类)。然而,当虚继承时,一般说来,派生类地址和其虚基类地址之间的偏移量是不固定的,因为如果这个派生类又被进一步继承的话,最终派生类会把共享的虚基类实例数据放到一个与上一层派生类不同的偏移量处。请看下例:
struct G : virtual C {
int g1;
void gf();
};
译者注:GdGvbptrG(In G, the displacement of G’s virtual base pointer to G)意思是:在G中,G对象的指针与G的虚基类表指针之间的偏移量,在此可见为0,因为G对象内存布局第一项就是虚基类表指针; GdGvbptrC(In G, the displacement of G’s
virtual base pointer to C)意思是:在G中,C对象的指针与G的虚基类表指针之间的偏移量,在此可见为8。
struct H : virtual C {
int h1;
void hf();
};
struct I : G, H {
int i1;
void _if();
};
暂时不追究vbptr成员变量从何而来。从上面这些图可以直观地看到,在G对象中,内嵌的C基类对象的数据紧跟在G的数据之后,在H对象中,内嵌的C基类对象的数据也紧跟在H的数据之后。但是,在I对象中,内存布局就并非如此了。VC++实现的内存布局中,G对象实例中G对象和C对象之间的偏移,不同于I对象实例中G对象和C对象之间的偏移。当使用指针访问虚基类成员变量时,由于指针可以是指向派生类实例的基类指针,所以,编译器不能根据声明的指针类型计算偏移,而必须找到另一种间接的方法,从派生类指针计算虚基类的位置。
在VC++中,对每个继承自虚基类的类实例,将增加一个隐藏的“位平台上,GdGvptrC是8个字节。同样,在I实例中的G对象实例也有“虚基类表指针”,不过该指针指向一个适用于“G处于I之中”的虚基类表,表中一项为IdGvbptrC,值为20。
观察前面的G、H和I,我们可以得到如下关于VC++虚继承下内存布局的结论:
· 首先排列非虚继承的基类实例;
· 有虚基类时,为每个基类增加一个隐藏的vbptr,除非已经从非虚继承的类那里继承了一个vbptr;
· 排列派生类的新数据成员;
· 在实例最后,排列每个虚基类的一个实例。
该布局安排使得虚基类的位置随着派生类的不同而“浮动不定”,但是,非虚基类因此也就凑在一起,彼此的偏移量固定不变。
C++虚继承初识的更多相关文章
- 虚基类&虚继承
发现这个月准备竞赛完全没有更新哎... 改了下某华大一c++测试题...网上对虚继承讲的要么太繁琐要么不到位,自力更生 #include<iostream> #include<fst ...
- C++继承,多重继承,虚继承的构造函数以及析构函数的调用顺序问题
#include <iostream> using namespace std; class A{ int data_a; public: A(){ data_a = ; cout < ...
- C++中虚继承派生类构造函数的正确写法
最近工作中某个软件功能出现了退化,追查下来发现是一个类的成员变量没有被正确的初始化.这个问题与C++存在虚继承的情况下派生类构造函数的写法有关.在此说明一下错误发生的原因,希望对更多的人有帮助. 我们 ...
- C++虚函数、虚继承、对象内存模型(转)
参考:http://blog.csdn.net/hxz_qlh/article/details/14633361 需要注意的是虚继承.多重继承时类的大小.
- 虚函数列表: 取出方法 // 虚函数工作原理和(虚)继承类的内存占用大小计算 32位机器上 sizeof(void *) // 4byte
#include <iostream> using namespace std; class A { public: A(){} virtual void geta(){ cout < ...
- C++对象模型:单继承,多继承,虚继承
什么是对象模型 有两个概念可以解释C++对象模型: 语言中直接支持面向对象程序设计的部分.对于各种支持的底层实现机制. 类中成员分类 数据成员分为静态和非静态,成员函数有静态非静态以及虚函数 clas ...
- C++中的虚继承 & 重载隐藏覆盖的讨论
虚继承这个东西用的真不多.估计也就是面试的时候会用到吧.. 可以看这篇文章:<关于C++中的虚拟继承的一些总结> 虚拟基类是为解决多重继承而出现的. 如:类D继承自类B1.B2,而类B1. ...
- C++ 多继承和虚继承的内存布局(转)
转自:http://www.oschina.net/translate/cpp-virtual-inheritance 警告. 本文有点技术难度,需要读者了解C++和一些汇编语言知识. 在本文中,我们 ...
- C++中的多重继承与虚继承的问题
1.C++支持多重继承,但是一般情况下,建议使用单一继承. 类D继承自B类和C类,而B类和C类都继承自类A,因此出现下图所示情况: A A \ / B C ...
随机推荐
- python 利用抛出异常并处理的优点
- React Native-组件的引用
之前文章中,我们使用了许多React Native组件,也定义了一些组件.但是我们都没有定义组件的标识,我们都是通过回调方法处理组件对应的事件,这种情况能满足绝大多数需求,有些情况我们需要对组件进行操 ...
- iOS如何才能在招聘中表现得靠谱?
http://www.cocoachina.com/programmer/20150707/12414.html 近一年内陆续面试了不少人了,从面试者到面试官的转变让我对 iOS 招聘有了更多的感受. ...
- Android学习笔记之 SimpleAdapter 中添加按钮响应事件,getView的重写
Andriod 里面的ListView是一个显示列表数据的控件,常用适配器SimpleAdapter进行绑定,绑定代码如下: ListView lstView = (ListView) this.fi ...
- js+canvas五子棋人机大战ai算法
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...
- jq 监听返回事件
<script> $(document).ready(function(e) { var counter = 0; if (window.hi ...
- 模板—tarjan求割边
int dfn[MAXN],low[MAXN],cnt; void tarjan(int x,int edg) { low[x]=dfn[x]=++cnt; for(int i=f(x);i;i=n( ...
- mysql数据库之单表查询
单标查询 单表查询语句 关键字执行的优先级 简单查询 where约束 group by 聚合函数 HAVING过滤 order by 查询排序 LIMIT限制查询的记录数 使用正则表达式查询 单表查询 ...
- python实用工具包
文本处理 FlashText 大规模关键字搜索利器,据说多余500个关键字时性能会明显优于正则表达式,暂未评测! 调试利器 pysnooper 不需要使用print进行调试
- 全面解读Python Web开发框架Django,利用Django构建web应用及其部署
全面解读Python Web开发框架Django Django是一个开源的Web应用框架,由Python写成.采用MVC的软件设计模式,主要目标是使得开发复杂的.数据库驱动的网站变得简单.Django ...