C++多重继承下,对象布局与编译器,是否为虚拟继承都有很大关系,下面将逐一分析其中的差别,相同点为都按照类继承的先后顺序布局(类内按照虚表、成员声明先后顺序排列)。该类情况为子类按照继承顺序排列,如class C:public B,public A此种情况布局如下:

如果class B,A带有虚函数,情况又发生了变化:

考虑下面的情况:

class D {
int d;
public:
virtual void fun() {}
virtual ~D() {}
}; class E {
public:
virtual void fun() {}
virtual ~E() {}
private:
int e;
}; class F:public D,public E {
public:
void fun() {D.fun();}
virtual ~F() {}
private:
int f;
};

类F函数的布局中无虚表(vs2010,20个字节):

class F	size(20):
+---
| +--- (base class D)
0 | | {vfptr}
4 | | d
| +---
| +--- (base class E)
8 | | {vfptr}
12 | | e
| +---
16 | f
+---

g++ 编译器中包含虚表:

Class F    	size=20 align=4   	base size=20 base align=4    F (0x2ea1740) 0
vptr=((& F::_ZTV1F) + 8u)
D (0x2e76b28) 0
primary-for F (0x2ea1740)
E (0x2e76b60) 8
vptr=((& F::_ZTV1F) + 28u)

2)  当出现基类重复继承情况

如非虚拟继承,重复继承,编译器会产生告警信息:

warning: direct base 'D' inaccessible in 'G' due to ambiguity [enabled by default]

编译器产生的布局与情况1)相同,都是按照继承的顺序排列

3) 虚拟继承(非菱形继承)

基类若非包含继承同一个父类,虚拟继承实际不存在,考虑下面的继承体系:

类F继承自类D,类G虚拟继承D,F,类的布局图如下(与情况1相同):

g++ -fdump-class-hierarchy -c multiInherit.cpp
Class H
size=32 align=4
base size=4 base align=4
H (0x2372b80) 0 nearly-empty
vptridx=0u vptr=((& H::_ZTV1H) + 16u)
D (0x231b9d8) 4 virtual
vptridx=4u vbaseoffset=-12 vptr=((& H::_ZTV1H) + 40u)
F (0x2372bc0) 12 virtual
vptridx=8u vbaseoffset=-16 vptr=((& H::_ZTV1H) + 68u)
D (0x231ba10) 12
primary-for F (0x2372bc0) E (0x231ba48) 20 vptridx=12u vptr=((& H::_ZTV1H) + 88u)

vs2010 编译布局与cl -d1reportAllClassLayout multiInherit.cpp,与类G布局不同,类H这里产生了虚表:

class H	size(32):
+---
0 | {vbptr}
+---
+--- (virtual base D)
4 | {vfptr}
8 | d
+---
+--- (virtual base F)
| +--- (base class D)
12 | | {vfptr}
16 | | d
| +---
| +--- (base class E)
20 | | {vfptr}
24 | | e
| +---
28 | f
+---

4) 虚拟继承(菱形继承)

菱形继承情况如下

无论类K是否虚拟自I,J,g++产生的类布局都相似(基类都产生虚表,不过地址指向同一内存):

g++ -fdump-class-hierarchy -c multiInherit.cpp
Class K
size=16 align=4
base size=8 base align=4
K (0x237f340) 0
vptridx=0u vptr=((& K::_ZTV1K) + 12u)
I (0x237f380) 0 nearly-empty
primary-for K (0x237f340)
subvttidx=4u
D (0x231bc40) 8 virtual
vptridx=20u vbaseoffset=-12 vptr=((& K::_ZTV1K) + 56u)
J (0x237f3c0) 4 nearly-empty
subvttidx=12u vptridx=24u vptr=((& K::_ZTV1K) + 32u)
D (0x231bc40) alternative-path

类L的布局如下:

Class L
size=16 align=4
base size=4 base align=4
L (0x237f680) 0 nearly-empty
vptridx=0u vptr=((& L::_ZTV1L) + 24u)
I (0x237f6c0) 0 nearly-empty virtual
primary-for L (0x237f680)
subvttidx=16u vptridx=4u vbaseoffset=-20
D (0x231bd58) 4 virtual
vptridx=8u vbaseoffset=-12 vptr=((& L::_ZTV1L) + 48u)
J (0x237f700) 12 nearly-empty virtual
subvttidx=24u vptridx=12u vbaseoffset=-24 vptr=((& L::_ZTV1L) + 76u)
D (0x231bd58) alternative-path

VS 2010产生的类布局与是否虚拟继承有很大关系,未虚拟继承将不产生虚表且相同基类排列在子类下面而非其子类的父类下:

cl -d1reportAllClassLayout multiInherit.cpp
class K size(16):
+---
| +--- (base class I)
0 | | {vbptr}
| +---
| +--- (base class J)
4 | | {vbptr}
| +---
+---
+--- (virtual base D)
8 | {vfptr}
12 | d
+---

类L的布局产生了虚表,且重新排列了基类顺序:

class L	size(20):
+---
0 | {vbptr}
+---
+--- (virtual base D)
4 | {vfptr}
8 | d
+---
+--- (virtual base I)
12 | {vbptr}
+---
+--- (virtual base J)
16 | {vbptr}
+---

C++ 类继承的对象布局的更多相关文章

  1. 【JavaScript】类继承(对象冒充)和原型继承__深入理解原型和原型链

    JavaScript里的继承方式在很多书上分了很多类型和实现方式,大体上就是两种:类继承(对象冒充)和原型继承. 类继承(对象冒充):在函数内部定义自身的属性的方法,子类继承时,用call或apply ...

  2. C++类继承内存布局(一)

    转自:http://blog.csdn.net/jiangyi711/article/details/4890889# 一 类布局 不同的继承方式将导致不同的内存布局 1)C结构 C++基于C,所以C ...

  3. Visual C++ 8.0对象布局的奥秘:虚函数、多继承、虚拟继承(VC直接输出内存布局)

    原文:VC8_Object_Layout_Secret.html 哈哈,从M$ Visual C++ Team的Andy Rich那里又偷学到一招:VC8的隐含编译项/d1reportSingleCl ...

  4. VC++对象布局的奥秘:虚函数、多继承、虚拟继承

    哈哈,从M$ Visual C++ Team的Andy Rich那里又偷学到一招:VC8的隐含编译项/d1reportSingleClassLayout和/d1reportAllClassLayout ...

  5. C++继承 派生类中的内存布局(单继承、多继承、虚拟继承)

    今天在网上看到了一篇写得非常好的文章,是有关c++类继承内存布局的.看了之后获益良多,现在转在我自己的博客里面,作为以后复习之用. ——谈VC++对象模型(美)简.格雷程化    译 译者前言 一个C ...

  6. 浅析GCC下C++多重继承 & 虚拟继承的对象内存布局

    继承是C++作为OOD程序设计语言的三大特征(封装,继承,多态)之一,单一非多态继承是比较好理解的,本文主要讲解GCC环境下的多重继承和虚拟继承的对象内存布局. 一.多重继承 先看几个类的定义: 01 ...

  7. Python类,域,方法,对象,继承

    类和对象: 是面向对象编程的两个主要方面,类创建一个新类型,而对象这个类的实例.. 域: 属于一个对象或类的变量被称为域.域有两种类型: 属于每个实例(类的对象)或属于类本身.它们分别被称为实例变量和 ...

  8. 虚继承之单继承的内存布局(VC在编译时会把vfptr放到类的头部,这和Delphi完全一致)

    C++2.0以后全面支持虚函数与虚继承,这两个特性的引入为C++增强了不少功能,也引入了不少烦恼.虚函数与虚继承有哪些特性,今天就不记录了,如果能搞了解一下编译器是如何实现虚函数和虚继承,它们在类的内 ...

  9. C++类继承中,基类/当前对象属性/当前对象的构造顺序

    [1]中提到,规范的派生类构造函数三个要点: 首先创建基类对象 应通过成员初始化列表,创建基类对象 应该初始化本派生类新增的成员变量 那在构造派生类实例的过程中,其基类(以及多继承的时候多个基类)/当 ...

随机推荐

  1. scala getter and setter

    package exp { object Main { def main(args: Array[String]): Unit = { B.name ="Fred"; printl ...

  2. 忠告初学者学习Linux系统的8点建议

    导读 新手或者说即将要入坑的小伙伴们,常常在QQ群或者在Linux论坛问一些问题,不过,其中大多数的问题都是很基础的.例如:如何给添加的用户归属用户组,复制整个文件到另一个目录下面,磁盘合理划分,甚至 ...

  3. Haskell Types与Typeclasses

    可使用 :t 命令检测表达式类型. 明确的类型首字母必大写. 一.Types Char Bool Int(有界,与Integer类型对比效率高) Integer(无界,与Int类型对比效率低) Flo ...

  4. [SCOI2007] 修车

    属于我的费用流版本终于诞生了!想来还有点小激动呢…看了下模板,之后完全按照自己的想象来写,这样在考场上也不怕啦~ 某人说其实费用流就是把Dinic里的BFS换成SPFA,似乎还是比较有道理的,就是ad ...

  5. .netcore跨平台 之 windows上编译,ubuntu上运行

    1 下载并安装netcore sdk    下载地址 https://github.com/dotnet/cli 选取合适的版本下载安装即可 打开 CMD ,输入dotnet,出现以下信息说明已安装好 ...

  6. vs连接服务器sql server数据库 web.config和代码

    方法一.在web.config里面配置,后连接数据库 (1)web.config文件:加在<connectionStrings>和</connectionStrings> 之间 ...

  7. 初探canvas

    canvas是html5新增的一个专用于图形处理的标签,利用canvas可以实现大部分图形操作canvas的一些基本操作与其他图形编程工具类似,包含:各种形状的边框.路径绘制和填充,画布属性调整,样式 ...

  8. IOS开发:监听来电状态的改变。

    #import <CoreTelephony/CTCallCenter.h> #import <CoreTelephony/CTCall.h> @property(nonato ...

  9. multipath tcp experiment

    git clone https://github.com/Neohapsis/mptcp-abuse.git sudo apt-get install python-pip sudo pip inst ...

  10. 移动混合开发之android文件管理新建文件和删除文件

    今天经过一天超过8小时的实践,有很多CSS上的细节需要注意: 1, /*注意是对before的操作*/ .content ul li .icon-check-empty:before{ display ...