C++ 继承之虚继承与普通继承的内存分布
仅供互相学习,请勿喷,有观点欢迎指出~
class A
{
virtual void aa(){};
};
class B : public virtual A
{
char j[]; //加入一个变量是为了看清楚class中的vfptr放在什么位置
public:
virtual void bb(){};
};
class C : public virtual A
{
char i[];
public:
virtual void cc(){};
};
class C1 : public A
{
char i1[];
public:
virtual void cc1(){};
};
class D : public C
{
char d[];
public:
virtual void dd(){};
};
class D1 : public C1
{
char d1[];
public:
virtual void dd1(){};
};
class E : public virtual C
{
char e[];
public:
virtual void ee(){};
};
class E1 : public virtual C1
{
char e1[];
public:
virtual void ee1(){};
};
class F : public C, public virtual B
{
char f[];
public:
virtual void ff(){};
};
class F1 : public virtual C, public B
{
char f1[];
public:
virtual void ff1(){};
};
class G : public virtual E
{
char g[];
public:
virtual void gg(){};
};
class H : public virtual E, public virtual C
{
char h[];
public:
virtual void hh(){};
};
class H1 : public E, public C1
{
char h1[];
public:
virtual void hh1(){};
};
在VS2013下,在项目->属性->C/C++->命令行中添加/d1reportAllClassLayout即可查看所有类的内存分布情况:
> class A size():
> +---
> | {vfptr}
> +---
>
> A::$vftable@:
> | &A_meta
> |
> | &A::aa
>
> A::aa this adjustor:
>
>
> class B size():
> +---
> | {vfptr}
> | {vbptr}
> | j
> | <alignment member> (size=)
> +---
> +--- (virtual base A)
> | {vfptr}
> +---
>
> B::$vftable@B@:
> | &B_meta
> |
> | &B::bb
>
> B::$vbtable@:
> | -
> | (Bd(B+)A)
>
> B::$vftable@A@:
> | -
> | &A::aa
>
> B::bb this adjustor:
>
> vbi: class offset o.vbptr o.vbte fVtorDisp
> A
>
>
> class C size():
> +---
> | {vfptr}
> | {vbptr}
> | i
> | <alignment member> (size=)
> +---
> +--- (virtual base A)
> | {vfptr}
> +---
>
> C::$vftable@C@:
> | &C_meta
> |
> | &C::cc
>
> C::$vbtable@:
> | -
> | (Cd(C+)A)
>
> C::$vftable@A@:
> | -
> | &A::aa
>
> C::cc this adjustor:
>
> vbi: class offset o.vbptr o.vbte fVtorDisp
> A
>
>
> class C1 size():
> +---
> | +--- (base class A)
> | | {vfptr}
> | +---
> | i1
> | <alignment member> (size=)
> +---
>
> C1::$vftable@:
> | &C1_meta
> |
> | &A::aa
> | &C1::cc1
>
> C1::cc1 this adjustor:
>
>
> class D size():
> +---
> | +--- (base class C)
> | | {vfptr}
> | | {vbptr}
> | | i
> | | <alignment member> (size=)
> | +---
> | d
> | <alignment member> (size=)
> +---
> +--- (virtual base A)
> | {vfptr}
> +---
>
> D::$vftable@C@:
> | &D_meta
> |
> | &C::cc
> | &D::dd
>
> D::$vbtable@:
> | -
> | (Dd(C+)A)
>
> D::$vftable@A@:
> | -
> | &A::aa
>
> D::dd this adjustor:
>
> vbi: class offset o.vbptr o.vbte fVtorDisp
> A
>
>
> class D1 size():
> +---
> | +--- (base class C1)
> | | +--- (base class A)
> | | | {vfptr}
> | | +---
> | | i1
> | | <alignment member> (size=)
> | +---
> | d1
> | <alignment member> (size=)
> +---
>
> D1::$vftable@:
> | &D1_meta
> |
> | &A::aa
> | &C1::cc1
> | &D1::dd1
>
> D1::dd1 this adjustor:
>
>
> class E size():
> +---
> | {vfptr}
> | {vbptr}
> | e
> | <alignment member> (size=)
> +---
> +--- (virtual base A)
> | {vfptr}
> +---
> +--- (virtual base C)
> | {vfptr}
> | {vbptr}
> | i
> | <alignment member> (size=)
> +---
>
> E::$vftable@:
> | &E_meta
> |
> | &E::ee
>
> E::$vbtable@E@:
> | -
> | (Ed(E+)A)
> | (Ed(E+)C)
>
> E::$vftable@A@:
> | -
> | &A::aa
>
> E::$vftable@C@:
> | -
> | &C::cc
>
> E::$vbtable@C@:
> | -
> | - (Ed(C+)A)
>
> E::ee this adjustor:
>
> vbi: class offset o.vbptr o.vbte fVtorDisp
> A
> C
>
>
> class E1 size():
> +---
> | {vfptr}
> | {vbptr}
> | e1
> | <alignment member> (size=)
> +---
> +--- (virtual base C1)
> | +--- (base class A)
> | | {vfptr}
> | +---
> | i1
> | <alignment member> (size=)
> +---
>
> E1::$vftable@E1@:
> | &E1_meta
> |
> | &E1::ee1
>
> E1::$vbtable@:
> | -
> | (E1d(E1+)C1)
>
> E1::$vftable@C1@:
> | -
> | &A::aa
> | &C1::cc1
>
> E1::ee1 this adjustor:
>
> vbi: class offset o.vbptr o.vbte fVtorDisp
> C1
>
>
> class F size():
> +---
> | +--- (base class C)
> | | {vfptr}
> | | {vbptr}
> | | i
> | | <alignment member> (size=)
> | +---
> | f
> | <alignment member> (size=)
> +---
> +--- (virtual base A)
> | {vfptr}
> +---
> +--- (virtual base B)
> | {vfptr}
> | {vbptr}
> | j
> | <alignment member> (size=)
> +---
>
> F::$vftable@C@:
> | &F_meta
> |
> | &C::cc
> | &F::ff
>
> F::$vbtable@C@:
> | -
> | (Fd(C+)A)
> | (Fd(F+)B)
>
> F::$vftable@A@:
> | -
> | &A::aa
>
> F::$vftable@B@:
> | -
> | &B::bb
>
> F::$vbtable@B@:
> | -
> | - (Fd(B+)A)
>
> F::ff this adjustor:
>
> vbi: class offset o.vbptr o.vbte fVtorDisp
> A
> B
>
>
> class F1 size():
> +---
> | +--- (base class B)
> | | {vfptr}
> | | {vbptr}
> | | j
> | | <alignment member> (size=)
> | +---
> | f1
> | <alignment member> (size=)
> +---
> +--- (virtual base A)
> | {vfptr}
> +---
> +--- (virtual base C)
> | {vfptr}
> | {vbptr}
> | i
> | <alignment member> (size=)
> +---
>
> F1::$vftable@B@:
> | &F1_meta
> |
> | &B::bb
> | &F1::ff1
>
> F1::$vbtable@B@:
> | -
> | (F1d(B+)A)
> | (F1d(F1+)C)
>
> F1::$vftable@A@:
> | -
> | &A::aa
>
> F1::$vftable@C@:
> | -
> | &C::cc
>
> F1::$vbtable@C@:
> | -
> | - (F1d(C+)A)
>
> F1::ff1 this adjustor:
>
> vbi: class offset o.vbptr o.vbte fVtorDisp
> A
> C
>
>
> class G size():
> +---
> | {vfptr}
> | {vbptr}
> | g
> | <alignment member> (size=)
> +---
> +--- (virtual base A)
> | {vfptr}
> +---
> +--- (virtual base C)
> | {vfptr}
> | {vbptr}
> | i
> | <alignment member> (size=)
> +---
> +--- (virtual base E)
> | {vfptr}
> | {vbptr}
> | e
> | <alignment member> (size=)
> +---
>
> G::$vftable@G@:
> | &G_meta
> |
> | &G::gg
>
> G::$vbtable@:
> | -
> | (Gd(G+)A)
> | (Gd(G+)C)
> | (Gd(G+)E)
>
> G::$vftable@A@:
> | -
> | &A::aa
>
> G::$vftable@C@:
> | -
> | &C::cc
>
> G::$vbtable@C@:
> | -
> | - (Gd(C+)A)
>
> G::$vftable@E@:
> | -
> | &E::ee
>
> G::$vbtable@E@:
> | -
> | - (Gd(E+)A)
> | - (Gd(E+)C)
>
> G::gg this adjustor:
>
> vbi: class offset o.vbptr o.vbte fVtorDisp
> A
> C
> E
>
>
> class H size():
> +---
> | {vfptr}
> | {vbptr}
> | h
> | <alignment member> (size=)
> +---
> +--- (virtual base A)
> | {vfptr}
> +---
> +--- (virtual base C)
> | {vfptr}
> | {vbptr}
> | i
> | <alignment member> (size=)
> +---
> +--- (virtual base E)
> | {vfptr}
> | {vbptr}
> | e
> | <alignment member> (size=)
> +---
>
> H::$vftable@H@:
> | &H_meta
> |
> | &H::hh
>
> H::$vbtable@:
> | -
> | (Hd(H+)A)
> | (Hd(H+)C)
> | (Hd(H+)E)
>
> H::$vftable@A@:
> | -
> | &A::aa
>
> H::$vftable@C@:
> | -
> | &C::cc
>
> H::$vbtable@C@:
> | -
> | - (Hd(C+)A)
>
> H::$vftable@E@:
> | -
> | &E::ee
>
> H::$vbtable@E@:
> | -
> | - (Hd(E+)A)
> | - (Hd(E+)C)
>
> H::hh this adjustor:
>
> vbi: class offset o.vbptr o.vbte fVtorDisp
> A
> C
> E
>
>
> class H1 size():
> +---
> | +--- (base class E)
> | | {vfptr}
> | | {vbptr}
> | | e
> | | <alignment member> (size=)
> | +---
> | +--- (base class C1)
> | | +--- (base class A)
> | | | {vfptr}
> | | +---
> | | i1
> | | <alignment member> (size=)
> | +---
> | h1
> | <alignment member> (size=)
> +---
> +--- (virtual base A)
> | {vfptr}
> +---
> +--- (virtual base C)
> | {vfptr}
> | {vbptr}
> | i
> | <alignment member> (size=)
> +---
>
> H1::$vftable@E@:
> | &H1_meta
> |
> | &E::ee
> | &H1::hh1
>
> H1::$vftable@C1@:
> | -
> | &A::aa
> | &C1::cc1
>
> H1::$vbtable@E@:
> | -
> | (H1d(E+)A)
> | (H1d(E+)C)
>
> H1::$vftable@A@:
> | -
> | &A::aa
>
> H1::$vftable@C@:
> | -
> | &C::cc
>
> H1::$vbtable@C@:
> | -
> | - (H1d(C+)A)
>
> H1::hh1 this adjustor:
>
> vbi: class offset o.vbptr o.vbte fVtorDisp
> A
> C
总结出单继承内存分布大致如下:
- 普通继承情况下,先父类元素后子类元素,若父类元素本身也是从某个爷爷类继承而来:父类是虚继承而来,则先父后子再爷爷(其实这个分布是满足规则2的),即爷爷放在最后;父类是普通继承而来,先爷爷后父再子,即爷爷放在前面。
- 虚继承情况下,先子类元素后父类元素,如果父类元素本身也是从某个爷爷类继承(不论是虚继承还是普通继承)而来,则父类由类的深到浅依次分布(先爷爷后父,爷爷在子和父之间)。
多继承的内存分布情况如下:
- 虚继承和普通继承同时存在的情况下,先进行普通继承,再进行虚继承。
- 继承都是虚继承或者普通继承的时候,按照从左到右声明顺序进行继承,注意,都是虚继承的话,如果继承的相同的类,这个类只会在最先出现的地方出现一次而已;如果是普通继承,相同的类会多次出现。
总之,先满足基本的父子类分布情况,如果父类还有更深层次的基类,这些基类再依据普通继承和虚继承的情况进行内存分布(即红字标注部分)。另外,具体内存分布情况会不会还与编译环境有关就不得而知了,至少sizeof类的大小是会与编译环境有关的(http://www.cnblogs.com/yanqi0124/p/3829964.html文章最后对比了gcc和VC下sizeof的不同,因为对虚表指针的处理方式不同)
根据上述解释,就能解释程序员面试宝典中的一题:
class A
{
virtual aa(){};
};
class B : public virtual A
{
char j[]; //加入一个变量是为了看清楚class中的vfptr放在什么位置
public:
virtual bb(){};
};
class C : public virtual B
{
char i[];
public:
virtual cc(){};
};

C++ 继承之虚继承与普通继承的内存分布的更多相关文章
- c++内存分布之虚函数(单一继承)
系列 c++内存分布之虚函数(单一继承) [本文] c++内存分布之虚函数(多继承) 结论 1.虚函数表指针 和 虚函数表 1.1 影响虚函数表指针个数的因素只和派生类的父类个数有关.多一个父类,派生 ...
- c++内存分布之虚析构函数
关于 本文代码演示环境: VS2017+32程序 虚析构函数是一种特殊的虚函数,可以知道,虚函数影响的内存分布规律应该也适用虚析构函数.看看实际结果. Note,一个类中,虚析构函数只能有一个. 本文 ...
- 【C++】继承(虚基类)
类的继承与派生 面向对象技术强调软件的可重用性,这种重用性通过继承机制来实现.而在类的继承过程中,被重用的原有类称为基类,新创建的类称为派生类.派生类定义语法格式如下: class <派生类名& ...
- C++浅析——继承类内存分布和虚析构函数
继承类研究 1. Code 1.1 Cbase, CTEST为基类,CTest2为其继承类,并重新申明了基类中的同名变量 class CBase { public: int Data; CBase() ...
- c/c++: c++继承 内存分布 虚表 虚指针 (转)
http://www.cnblogs.com/DylanWind/archive/2009/01/12/1373919.html 前部分原创,转载请注明出处,谢谢! class Base { pu ...
- C++对象模型:单继承,多继承,虚继承
什么是对象模型 有两个概念可以解释C++对象模型: 语言中直接支持面向对象程序设计的部分.对于各种支持的底层实现机制. 类中成员分类 数据成员分为静态和非静态,成员函数有静态非静态以及虚函数 clas ...
- C++ 多继承和虚继承的内存布局(转)
转自:http://www.oschina.net/translate/cpp-virtual-inheritance 警告. 本文有点技术难度,需要读者了解C++和一些汇编语言知识. 在本文中,我们 ...
- 从零开始学C++之虚继承和虚函数对C++对象内存模型造成的影响
首先重新回顾一下关于类/对象大小的计算原则: 类大小计算遵循结构体对齐原则 第一个数据成员放在offset为0的位置 其它成员对齐至min(sizeof(member),#pragma pack(n) ...
- C++在单继承、多继承、虚继承时,构造函数、复制构造函数、赋值操作符、析构函数的执行顺序和执行内容
一.本文目的与说明 1. 本文目的:理清在各种继承时,构造函数.复制构造函数.赋值操作符.析构函数的执行顺序和执行内容. 2. 说明:虽然复制构造函数属于构造函数的一种,有共同的地方,但是也具有一定的 ...
随机推荐
- 1016: [JSOI2008]最小生成树计数 - BZOJ
Description 现在给出了一个简单无向加权图.你不满足于求出这个图的最小生成树,而希望知道这个图中有多少个不同的最小生成树.(如果两颗最小生成树中至少有一条边不同,则这两个最小生成树就是不同的 ...
- centos7安装chrome及加载poatman开发插件
为什么要安装chrome?因为centos7的默认浏览器firefox的实在是不习惯,上面占了太多,本来显示器就不大... 好了,首先下载chome的rpm安装包(如果需要的可以留言,我有备份) 然后 ...
- 原生js解决跨浏览器兼容问题
//跨浏览器兼容问题 Util = { //添加类名 add : function(ele,type,hand){ if(ele.addEventListener){ ele.addEventList ...
- Matlab划分测试集和训练集
% x是原数据集,分出训练样本和测试样本 [ndata, D] = size(X); %ndata样本数,D维数 R = randperm(ndata); %1到n这些数随机打乱得到的一个随机数字序列 ...
- HDU 1429 胜利大逃亡(续)(三维BFS)
题目链接 题意 : 中文题不详述. 思路 : 这个题和1885差不多一样的,所以我直接改了改那个代码就交上了,链接 #include <stdio.h> #include <stri ...
- IntelliJ Idea12 破解码与中文乱码配置
user name:JavaDeveloper serial number:92547-KY2BB-QZ0S1-PEZCV-HUT8Q-6RYY4 会出现Ok可以点击就会将软件 安装后, ...
- 关于static继承的问题
c++primer 15.2.7节关于static继承的意思是,父类和子类共享static函数或者static成员变量,并且子类要访问还要受它们的权限限制,下面是看到的另一个例子 class Base ...
- 将 Qt 5.6 集成至 VS2015
摘要: 由于VS2015不再支持addin,所以要用其他手段. 这里给出64位系统下的安装步骤,32位类似. 一.安装VS2015 过程略.值得注意的是要选择需要安装的内容,既然要用Qt,那么C++相 ...
- inflate方法与findViewById的区别
LayoutInflater作用是将layout的xml布局文件实例化为View类对象. 对于一个没有被载入或者想要动态载入的界面,都需要使用LayoutInflater.inflate()来找 re ...
- Python之数据结构篇
简介: 数据结构是可以处理一些数据的结构,或者说,他们是用来存储一组相关数据的.在python中有三种内建的数据结构,分别是列表.元组合字典.我们将会学习如何使用它们是编程变得简单. 列表 list是 ...