转载自http://blog.csdn.net/it_yuan/article/details/24651347

#类中的元素

0. 成员变量   1. 成员函数   2. 静态成员变量   3. 静态成员函数   4. 虚函数   5. 纯虚函数

#影响对象大小的因素

0. 成员变量     1. 虚函数表指针(_vftptr)   2. 虚基类表指针(_vbtptr)   3. 内存对齐

_vftptr、_vbtptr的初始化由对象的构造函数, 赋值运算符自动完成;对象生命周期结束后,由对象的析构函数来销毁。
对象所关联的类型(type_info),通常放在virtual table的第一个slot中。

虚继承:在继承定义中包含了virtual关键字的继承关系;
虚基类:在虚继承体系中的通过virtual继承而来的基类,需要注意的是:
class CDerive : public virtual CBase {}; 其中CBase称之为CDerive的虚基类,而不是说CBase就是个虚基类,因为CBase还可以为不是虚继承体系中的基类。

虚函数被派生后,仍然为虚函数,即使在派生类中省去virtual关键字。

注:【下文中_vbptr即_vbtptr】

#对象内存布局分类讨论

vc6变量查看器中(Locals,Watch1等),也可以看到部分对象布局的情况(不完整,且虚继承是错误的)。

vs2005及以后版本的编译器提供了/d1reportSingleClassLayout[类名]编译选项来查看对象完整的内存布局:

cl classLayout.cpp /d1reportSingleClassLayoutCChildren

0. 单一类

(1). 空类

sizeof(CNull)=1(用于标识该对象)

(2). 只有成员变量的类

int nVarSize = sizeof(CVariable) = 12

内存布局:

(3). 只有虚函数的类

int nVFuntionSize = sizeof(CVFuction) = 4(虚表指针)

内存布局:

(4). 有成员变量、虚函数的类

int nParentSize = sizeof(CParent) = 8

内存布局:

1. 单一继承(含成员变量、虚函数、虚函数覆盖)

int nChildSize = sizeof(CChildren) = 12

vc中显示的结果(注:还有1个虚函数CChildren::g1没有被显示出来):

d1reportSingleClass查看:

内存布局:

2. 多继承 (含成员变量、虚函数、虚函数覆盖)

int nChildSize = sizeof(CChildren) = 20

vc中显示的结果(注:还有2个虚函数CChildren::f2,CChildren::h2没有被显示出来,this指针的adjustor[调整值]也没打印出):

d1reportSingleClass查看:

内存布局:

3. 深度为2的继承(含成员变量、虚函数、虚函数覆盖)

int nGrandSize = sizeof(CGrandChildren) = 24

vc中显示的结果(注:还有3个虚函数CGrandChildren::f2,CChildren::h2,CGrandChildren::f3没有显示出来,this指针的adjustor[调整值]也没打印出):

d1reportSingleClass查看:

内存布局:

4 重复继承(含成员变量、虚函数、虚函数覆盖)

int nGrandSize = sizeof(CGrandChildren) = 28

vc中显示的结果(注:还有大量的虚函数没有显示出来,this指针的adjustor[调整值]也没打印出):

thunk函数:一种形实转换辅助函数;主要做this指针调整,函数调用重定向。

d1reportSingleClass查看:

内存布局:

由于m_nAge在内容中存在两个拷贝,因此我们不能直接通过pGrandChildrenA->m_nAge来访问该变量,

这样会存在二义性,编译器无法知道应该访问CChildren1中的m_nAge,还是CChildren2中的m_nAge。

为了标识唯一的m_nAge,就需要带上其所在范围的类名了。如下:

1 pGrandChildrenA->CChildren1::m_nAge = 1;
2 pGrandChildrenA->CChildren2::m_nAge = 2;

5. 单一虚继承(含成员变量、虚函数、虚函数覆盖)

int nChildSize = sizeof(CChildren) = 20

d1reportSingleClass查看:

内存布局:

6. 多虚继承(含成员变量、虚函数、虚函数覆盖)

(1) virtual CParent1, CParent2

int nChildSize = sizeof(CChildren) = 24

d1reportSingleClass查看:

内存布局:

(2) CParent1, virtual CParent2

int nChildSize = sizeof(CChildren) = 24

d1reportSingleClass查看:

内存布局:

(3) virtual CParent1, virtual CParent2

int nChildSize = sizeof(CChildren) = 28

d1reportSingleClass查看:

内存布局:

7. 钻石型的虚拟多重继承(含成员变量、虚函数、虚函数覆盖)

int nGrandChildSize = sizeof(CGrandChildren) = 36

d1reportSingleClass查看:

thunk函数:一种形实转换辅助函数;主要做this指针调整,函数调用重定向。

内存布局:

#外部参考

C++类对应的内存结构

陈皓- C++ 虚函数表解析

陈皓 - C++ 对象的内存布局(上)

陈皓 - C++ 对象的内存布局(下)

参考链接:http://blog.csdn.net/yangshuangtao/article/details/45192149

c++内存对齐原理的更多相关文章

  1. C语言内存对齐原理

    一.什么是字节对齐,为什么要对齐? 现代计算机中内存空间都是按照byte划分的,从理论上讲似乎对任何类型的变量的访问可以从任何地址开始,但实际情况是在访问特定类型变量的时候经常在特定的内存地址访问,这 ...

  2. 重磅硬核 | 一文聊透对象在 JVM 中的内存布局,以及内存对齐和压缩指针的原理及应用

    欢迎关注公众号:bin的技术小屋 大家好,我是bin,又到了每周我们见面的时刻了,我的公众号在1月10号那天发布了第一篇文章<从内核角度看IO模型的演变>,在这篇文章中我们通过图解的方式以 ...

  3. C/C++: C++位域和内存对齐问题

    1. 位域: 1. 在C中,位域可以写成这样(注:位域的数据类型一律用无符号的,纪律性). struct bitmap { unsigned a : ; unsigned b : ; unsigned ...

  4. C语言再学习之内存对齐

    昨天看Q3的代码,看到有个_INTSAIZEOF的宏,着实晕了一阵.一番google后,终于明白,这个宏的作用是求出变量占用内存空间的大小,先看看_INTSAIZEOF的定义吧: #define _I ...

  5. C结构体中数据的内存对齐问题

    转自:http://www.cnblogs.com/qwcbeyond/archive/2012/05/08/2490897.html 32位机一般默认4字节对齐(32位机机器字长4字节),64位机一 ...

  6. 2的m次方 内存对齐

    在存储的时候,为了提高效率,一般都会让偏移量落在2的m次方的位置上,而且常有向上取整和向下取整两种需求.向下取整PALIGN_DOWN(x,align)  (x & (- align)) 这样 ...

  7. Net的struct的内存对齐问题

    很少有人谈起struct的内存对齐问题, 就是在很多C#书中, 也很少提及. 但在实际应用中, 如果不注意内存对齐, struct比较大的话, 则会浪费一定的内存.    先从一个实例看起. publ ...

  8. c++中类对象的内存对齐

    很多C++书籍中都介绍过,一个Class对象需要占用多大的内存空间.最权威的结论是: *非静态成员变量总合.(not static) *加上编译器为了CPU计算,作出的数据对齐处理.(c语言中面试中经 ...

  9. C/C++中的内存对齐问题和pragma pack命令详解

    这个内存对齐问题,居然影响到了sizeof(struct)的结果值.突然想到了之前写的一个API库里,有个API是向后台服务程序发送socket请求.其中的socket数据包是一个结构体.在发送soc ...

随机推荐

  1. C/C++内存分配和管理

    1. 内存分配和管理 1.1 malloc.calloc.realloc.alloca malloc:申请指定字节数的内存.申请到的内存中的初始值不确定. calloc:为指定长度的对象,分配能容纳其 ...

  2. spring中获取dao或对象中方法的实例化对象

    spring中获取dao的中方法的实例化对象: //获取应用上下文对象 ApplicationContext ctx = new ClassPathXmlApplicationContext(&quo ...

  3. 解析ArcGis的字段计算器(一)——数值型数据计算,从“面积计算”开始

    先来点儿背景知识铺垫: ArcMap的字段计算器提供了两种脚本语言的支持用以计算,两种脚本语言是VBScript与Python. 多数人选择使用前者,因为它的基本函数和Excel的函数貌似一样.注意我 ...

  4. [Android] Android 卡片式控件CardView的优雅使用

    [Android] Android 卡片式控件CardView的优雅使用 CardView是在安卓5.0提出的卡片式控件 其具体用法如下: 1.在app/build.gradle 文件中添加 comp ...

  5. Silverlight 样式的灵活使用

    众所周知,Silverlight将界面设计与代码实现分开.即便如此,如果不能灵活地运用样式Style,开发的效率依然会比较低.比如,针对类似的TextBlock,你可能需要反复地在设计器xaml中复制 ...

  6. 指定so动态链接库连接器

    在学习x86_64汇编时, 发现一旦使用glibc库函数, 如printf时, 一般是需要使用为ld传递命令行参数-lc来动态连接libc.so的, 但是, 生成的可执行文件却无法运行: 气煞我也! ...

  7. SpringBoot入门笔记(一)、HelloWorld

    本文是一篇SprintBoot学习入门笔记 1.打开Eclipse,版本为Oxygen 4.7.0 2.新建项目NewProject->MavenProject->Next->Nex ...

  8. 使用js弹出div刷新时闪烁解决方法

    <div style="visibility: hidden"> //弹出div内容 </div>

  9. GCC编译器原理(一)------GCC 工具:addr2line、ar、as、c++filt和elfedit

    1.3 GCC 工具 1.3.1 binutils 工具集 工具 描述 addr2line 给出一个可执行文件的内部地址,addr2line 使用文件中的调试信息将地址翻译成源代码文件名和行号. ar ...

  10. Windows打开文件

    cmd中, windows  打开文件命令:start: Linux       打开文件命令:open