C++类继承内存布局(一)
转自: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++类继承内存布局(一)的更多相关文章
- C++类继承内存布局(三)
参考:http://blog.csdn.net/jiangyi711/article/details/4890889# (三)成员函数 类X中每一个非静态成员函数都会接受一个特殊的隐藏参数——this ...
- C++类继承内存布局(二)
转自:http://blog.csdn.net/jiangyi711/article/details/4890889# (二 )成员变量 前面介绍完了类布局,接下来考虑不同的继承方式下,访问成员变量的 ...
- 继承虚函数浅谈 c++ 类,继承类,有虚函数的类,虚拟继承的类的内存布局,使用vs2010打印布局结果。
本文笔者在青岛逛街的时候突然想到的...最近就有想写几篇关于继承虚函数的笔记,所以回家到之后就奋笔疾书的写出来发布了 应用sizeof函数求类巨细这个问题在很多面试,口试题中很轻易考,而涉及到类的时候 ...
- [CPP] 类的内存布局
本文可以解决下面 3 个问题: 以不同方式继承之后,类的成员变量是如何分布的? 虚函数表及虚函数表指针,在可执行文件中的位置? 单一继承.多继承.虚拟继承之后,类的虚函数表的内容是如何变化的? 在这里 ...
- cl查看类的内存布局
查看单个类的内存布局 Microsoft Visual Studio编译器cl的编译选项可以查看源文件中某个C++类的内存布局,对于想了解某个对象的内存布局的人来说十分直观和方便. • 命令格式 ...
- c++类的内存布局
问题: 考察了reinterpret_cast和static_cast的区别.顺道发现了一个可以查看c++内存布局的工具(在VS中). 结果: 前两个输出的地址形同,后一个不同. class A{in ...
- VS2010下如何查看类的内存布局
用VS2010查看类的内存布局,这里用两种方法 (1)MSVC有个隐藏的"/d1"开关,通过这个开关可以查看项目中类的内存布局情况. 修改项目属性,添加"/d1 repo ...
- 【C++对象模型】使用gcc、clang和VC++显示C++类的内存布局
引言 各种C++实现对C++类/对象的内存布局可能有所不同,包括数据成员的顺序.虚函数表(virtual table: vtbl)的结构.继承关系的处理等.了解C++类/对象的布局,对于理解C++各种 ...
- c++中如何查看一个类的内存布局
打开VS command prompt,输入下述命令可以看到对象的内存布局. cl a.cpp -d1 reportSingleClassLayout[classname] // reportSin ...
随机推荐
- mac使用初级
imac使用的是login shell,所有开启一个terminal的时候,不会运行.bashrc文件,而是运行.bash_profile文件,因此只需要中home目录新建一个.bash_profil ...
- 问题-Delphi编译到最后Linking时总是出现与ntdll.dll有关的错误还有Fatal Error Out of memory错误
1.跳出错误法 ===================================================在主界面的implementation {$R *.dfm} 下放入以下代码: ...
- 问题-[DelphiXE7]新建的安桌模拟器运行程序闪退
问题现象:在DelphiXE7中的手机模拟器,每次运行程序,就闪退?问题处理:在 DelphiXE7的目录中,有一个AVD文件夹,删除后就好了.
- hdoj 3861 The King’s Problem【强连通缩点建图&&最小路径覆盖】
The King’s Problem Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Other ...
- nyoj 325 zb的生日
01背包 zb的生日 时间限制:3000 ms | 内存限制:65535 KB 难度:2 描述 今天是阴历七月初五,acm队员zb的生日.zb正在和C小加.never在武汉集训.他想给这两位兄 ...
- asp.net Mvc+bootstarp+esayUI+EF 框架(一)
"框架" 这两个字从通俗的意义来讲就是提高复用性,解耦类之间的关系和方便开发人员开发. 使用的技术也是大家基本现在都用过的,而这个系类我所要讲的内容是什么呢? 框架的基本 ...
- JAVA基础入门
Java入门基础 1.IDE->Eclipse 新建程序步骤 1.创建一个Java项目 2.创建一个包(package) 也就相当于C#中的命名空间C++中的头文件 3.创建一个类 这样就完成了 ...
- Kinect开发学习笔记之(一)Kinect介绍和应用
Kinect开发学习笔记之(一)Kinect介绍和应用 zouxy09@qq.com http://blog.csdn.net/zouxy09 一.Kinect简单介绍 Kinectfor Xbox ...
- jdk安装 java运行编译(不含语法)
一.开发的准备 1.jdk的安装(window) (1)根据自己的电脑下载对应的jdk,并安装 (推荐安装在没有中文的目录中). 网站 http://www.oracle.com/technetwor ...
- flex学习网站地址
http://hacker47.iteye.com/blog/213887 http://www.cuplayer.com/player/PlayerCode/Flex/ http://bbs.9ri ...