转自:http://blog.csdn.net/jiangyi711/article/details/4890889#

一 类布局

不同的继承方式将导致不同的内存布局

1)C结构

C++基于C,所以C++基本上兼容C。特别地,C++规范在“结构”上使用了和C相同的,简单的内存布局原则:成员变量按其被声明的顺序排列,按具体实现所规定的对齐原则在内存地址上对齐。

struct A {
char c;
int i;
};

从上图可见,A在内存中占有8个字节,按照声明成员的顺序,前4个字节包含一个字符(实际占用1个字节,3个字节空着,补对齐),后4个字节包含一个整数。A的指针就指向字符开始字节处。

2)有C++特征的结构:

C++本质上是面向对象的语言:包含继承、封装,以及多态

原始的C结构经过改造,成了面向对象世界的基石——类。

除了成员变量外,C++类还可以封装成员函数和其他东西。

C++类实例的大小完全取决于一个类及其基类的成员变量,以及为了实现虚函数和虚继承而引入的隐藏成员变量。成员函数基本上不影响类实例的大小。

struct B {
public:
int bm1;
protected:
int bm2;
private:
int bm3;
static int bsm;
void bf();
static void bsf();
typedef void* bpv;
struct N { };
};

这里B是一个C结构,然而,该结构有一些C++特征:控制成员可见性的public/protected/private关键字、成员函数、静态成员,以及嵌套的类型声明

实际上,只有成员变量才占用类实例的空间 

类中的成员函数存放在代码区,静态函数也存放在代码区,而不是静态区。静态成员函数与一般成员函数的唯一区别就是没有this指针,因此不能访问非静态数据成员

B中,为何static int bsm不占用内存空间?因为它是静态成员,该数据存放在程序的数据段中,不在类实例中

3)单继承

struct C
{
int c1;
void cf();
}; struct D : C
{
int d1;
void df();
};

派生类要保留基类所有的属性和行为,每个派生类的实例都包含了一份完整的基类实例数据

在D中,并不是说基类C的数据一定要放在D的数据之前,只不过这样放的话,能够保证D中的C对象地址,恰好是D对象地址的第一个字节

在这种安排下,有了派生类D的指针,要获得基类C的指针,就不必要计算偏移量了

即在单继承模式下,每个派生类都简单的把自己的成员变量添加到基类的成员变量之后

4)多重继承

struct C {
int c1;
void cf();
}; struct E {
int e1;
void ef();
}; struct F : C , E {
int f1;
void ff();
};

机构F从C和E多重继承得来,与单继承不同的是,F实例靠内了每个基类的所有数据。

与单继承不同的是,在多重继承下,内嵌的两个基类的对象指针不可能全都与派生类对象指针相同

VC++按照基类的声明顺序,先排列基类实例数据,最后才排列派生类实例数据,派生类数据本身也是按照声明顺序布局的(在有虚函数的情况下,这个规则有所不同)

5)虚继承

 考虑下面这种继承层次:

struct A {};
struct B :A {};
struct C :A {};
struct D :B ,C {};

则在D的实例中,将包含两个A的实例,这两个实例分别来自B和C,这导致了额外的内存开销,并且会造成混乱(对于D,不知道如何区分两个A的实例)

所以出现了虚继承

struct A {};
struct B : virtual A {};
struct C : virtual A {};
struct D : B , C {};

使用虚继承,比单继承和多重继承将有更大的实现开销和调用开销:

在单继承或多重继承下,内嵌的基类实例地址与派生类的实例地址相比,要么地址相同,要么相差一个固定的偏移量

当虚继承时,一般说来,派生类地址和其虚基类地址之间的偏移量是不固定的,因为派生类如果被进一步继承的话,最终派生类会把共享的虚基类实例数据放到一个与上一层派生类不同的偏移量处:

struct G : virtual C {
int g1;
void gf();
};

vbptr虚基类表指针:

GdGvbptrG:在G中G对象的指针与G的虚基类表指针之间的偏移量,在此可见为0,因为G对象内存布局第一项就是虚基类表指针

GdGvbptrC:在G中C对象的指针与G的虚基类表指针之间的偏移量,在此可见为8

struct H : virtual C {
int h1;
void hf();
};

struct I : G, H {
int i1;
void _if();
};

从上面这些图可以看出

在G对象中,内嵌的C基类的数据紧跟在G的数据之后,在H对象中,内嵌的C基类对象的数据紧很在H的数据之后,但在I对象中,内存的布局并非如此

在VC++中,对每个继承自虚基类的类实例,将增加一个隐藏的虚基类表指针成员变量,从而达到间接计算虚基类位置的目的。该变量指向一个全类共享的偏移量表,表中记录了对于该类而言,虚基类表指针与虚基类之间的偏移量

可以得到下列关于VC++虚拟继承下内存布局的结论:

1):首先排列非虚继承的基类实现

2):有虚基类时,为每个基类增加一个隐藏的vbptr指针,除非已经从非虚继承的类那里继承了一个vbptr

3):排列派生类的数据成员

4):在实例最后,排列每个虚基类的一个实例

C++类继承内存布局(一)的更多相关文章

  1. C++类继承内存布局(三)

    参考:http://blog.csdn.net/jiangyi711/article/details/4890889# (三)成员函数 类X中每一个非静态成员函数都会接受一个特殊的隐藏参数——this ...

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

    转自:http://blog.csdn.net/jiangyi711/article/details/4890889# (二 )成员变量 前面介绍完了类布局,接下来考虑不同的继承方式下,访问成员变量的 ...

  3. 继承虚函数浅谈 c++ 类,继承类,有虚函数的类,虚拟继承的类的内存布局,使用vs2010打印布局结果。

    本文笔者在青岛逛街的时候突然想到的...最近就有想写几篇关于继承虚函数的笔记,所以回家到之后就奋笔疾书的写出来发布了 应用sizeof函数求类巨细这个问题在很多面试,口试题中很轻易考,而涉及到类的时候 ...

  4. [CPP] 类的内存布局

    本文可以解决下面 3 个问题: 以不同方式继承之后,类的成员变量是如何分布的? 虚函数表及虚函数表指针,在可执行文件中的位置? 单一继承.多继承.虚拟继承之后,类的虚函数表的内容是如何变化的? 在这里 ...

  5. cl查看类的内存布局

    查看单个类的内存布局 Microsoft Visual Studio编译器cl的编译选项可以查看源文件中某个C++类的内存布局,对于想了解某个对象的内存布局的人来说十分直观和方便. • 命令格式    ...

  6. c++类的内存布局

    问题: 考察了reinterpret_cast和static_cast的区别.顺道发现了一个可以查看c++内存布局的工具(在VS中). 结果: 前两个输出的地址形同,后一个不同. class A{in ...

  7. VS2010下如何查看类的内存布局

    用VS2010查看类的内存布局,这里用两种方法 (1)MSVC有个隐藏的"/d1"开关,通过这个开关可以查看项目中类的内存布局情况. 修改项目属性,添加"/d1 repo ...

  8. 【C++对象模型】使用gcc、clang和VC++显示C++类的内存布局

    引言 各种C++实现对C++类/对象的内存布局可能有所不同,包括数据成员的顺序.虚函数表(virtual table: vtbl)的结构.继承关系的处理等.了解C++类/对象的布局,对于理解C++各种 ...

  9. c++中如何查看一个类的内存布局

    打开VS command prompt,输入下述命令可以看到对象的内存布局. cl a.cpp -d1 reportSingleClassLayout[classname] //  reportSin ...

随机推荐

  1. c语言 字符版 简易2048

    花了两个多小时,用最蠢的方法写的……最简陋版…… 还不确定这么写逻辑对不对…… #include <iostream> #include <cstdio> #include & ...

  2. hdoj 1977 Consecutive sum II

    Consecutive sum II Time Limit: 3000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Other ...

  3. 关于easyui模拟win2012桌面的一个例子系列

    最近时间比较充裕,想到之前领导问我,什么界面更适合公司这种屏幕小但是又要求可以同时处理更多的工作. 我感觉  windows是最合适的,毕竟微软已经做了这么多年的系统了,人的操作习惯已经被他们确定了. ...

  4. oracle数据库自动备份脚本

    ::通过exp命令导出远程机器(192.168.2.1)上指定服务(orcl)指定用户(pmis)及密码(pmis)的数据 ::运行该脚本的机器必须安装oracle @echo off @echo [ ...

  5. jQuery的DOM操作小案例

    案例一:下拉列表左右选择 <body> <div> <select style="width:60px" multiple size="10 ...

  6. C++或者C#中如何拿到一个窗口的标题

    博客搬到了fresky.github.io - Dawei XU,请各位看官挪步.最新的一篇是:C++或者C#中如何拿到一个窗口的标题.

  7. open/close table on mysql

    http://hidba.org/?p=170   我们知道mysql是一个支持多线程的数据库,尤其在innodb存储引擎出现后,对mysql的事务,并发,锁支持得到了极大提高.在高并发的访问的应用场 ...

  8. textLayout_1.0.0.595.swz

    使用ai制作的矢量素材,导出到flash里面.生成swf时.有的时候会多一个textLayout_1.0.0.595.swz的文件. 这会导致导出的swf无法加载使用.会显示不出来. 解决办法是: 检 ...

  9. 编写Qt Designer自定义控件(一)——如何创建并使用Qt自定义控件

    在使用Qt Designer设计窗体界面时,我们可以使用Widget Box里的窗体控件非常方便的绘制界面,比如拖进去一个按钮,一个文本编辑器等.虽然Qt Designer里的控件可以满足我们大部分的 ...

  10. 【转载】soapui基于持续集成工具自动化运行的调研姿势

    soapui中的testrunner.bat调研姿势,用于自动化测试副标题:soapui基于持续集成工具自动化运行的调研姿势 各位亲爱的同仁们,大家好吗?最近项目在搞持续集成工具,我们的测试用例都是基 ...