如果一个类只定义了类名,没定义任何方法和字段,如class A{};那么class A的每个实例占用1个字节的内存,编译器会会在这个其实例中安插一个char,以保证每个A实例在内存中有唯一的地址,如A a,b;&a!=&b。如果一个直接或是间接的继承(不是虚继承)了多个类,如果这个类及其父类像A一样没有方法没有字段,那么这个类的每个实例的大小都是1字节,如果有虚继承,那就不是1字节了,每虚继承一个类,这个类的实例就会多一个指向被虚继承父类的指针。还有一点值得说明的就是像A这样的类,编译器不一定会产生传说中的那6个方法,这些方法只会在需要的时候产生,如class  A没有被任何地方使用那这些方法编译器就没有必要产生,如果这个类实例化了,那么会产生default constructor,而destructor则不一定产生。

如果一个类中有static data member,nonstatic data member,还有const data member,enum,那么它的内存布局会是什么样的呢,看下面简单的类Point:

class Point
{
public:
Point():maxCount(){}
private:
int X;
static int count;
int Y;
const int maxCount ;
enum{
minCount=
};
};

Sizeof(Point)=12,为什么占12字节呢,我相信很多人都知道是哪几个成员变量占用的,就是X,Y,maxCount,maxCount作为常量字段,但在Point的每个实例中可能有不同的值,当然属于Point实例的一部分,如果把maxCount定义成static,那它就不不是Point实例的一部分了,如果定义成static  const int maxCount=1;则maxCount分配在.data段中,如果没有初始化则分配在.bss段中,反正跟Point的实例无关,count分配在.bss段中,minCount分配在.rdata段中,总之count,maxCount,minCount在编译连接完成之后,内存(虚拟地址)就分配好了,在程序加载的时候,会把他们的虚拟地址对应上实际的物理地址。

Data member的内存布局:nonstatic data member在class object中的顺序和其申明的顺序一样,static data  member和const member不在class object中因为他们只有一份,被class object共享,所以static data member和const data member,枚举并不会响应class object的大小。关于段的信息,我觉得是每个C/C++程序员必须知道的。而Point每次实例化的时候则只需要分配X,Y,maxCount需要的内存。

每个类的data member在内存中应该是连续的,如果出现数据对齐的情况,可能中间会有空白地带。请看下面几个类:

class AA
{
protected:
int X;
char a;
}; class BB:public AA
{
protected:
char b;
}; class CC:public BB
{
protected:
char c;
};

Sizeof(AA)=8//对齐3字节

Sizeof(BB)=12//两个3字节对齐

Sizeof(CC)=16//编译器“无耻”的用了3个3字节对齐

关于内存对齐问题我相信大家经常遇到,我就不废话,我之前写过一篇关于内存对齐的文章《数据对齐

编译器为什么要无耻的在class CC中加3个3字节对齐呢,这样每个CC的实例就大了9字节。如果编译器不加这9字节的空白,那么CC的每个实例就是8字节,前面的X占4字节,后面的a,b,c占3字节,加1字节的空白对齐,刚好8字节,没有谁很傻很天真的以为最好是占7字节吧。

如果CC占用8字节内存,同样的AA,BB都是8字节的内存,这样的话,如果把一个指向AA实例的指针赋给一个指向CC实例的指针,那么就会把AA中的8字节直接盖到CC的8字节上,结果CC实例中的b,c都被赋上了不是我们想要的值,这很可能会导致你的程序出问题。

父类的data member会在子类的实例中有完整的一份,这样在有继承关系的类之间进行类型转换,就只用简单的修改指针的指向。

Data Member的存取。对一个data member的存取,编译器把对象实例的起始地址加上data member的偏移量。如CC c;

c.X=1;相当于&c+(&CC::X-1),减一其实是为了区分是指向object的指针还是指向data member的指针,指向data member的要减一。每一个data member的偏移量在编译的时候是知道的,根据成员变量的类型和内存对齐,存在virtual继承或是虚方法的情况编译器会自动加上一些辅助的指针,如指向虚方法的指针,指向虚继承父类的指针等。

在data member的存取效率上,struct member 、class member、单一继承或是多重继承的情况下效率都是一样的,因为他们的存储其实都是&obj+(&class.datamember-1)。在虚继承的情况下,可能会影响存储性能,如通过一个指针来存取一个指向虚继承而来的data member,那么性能会有影响,因为在虚继承的时候,在编译的时候还不能确定这个data member是来自子类还是父类,只有在运行的时候才能推断出来,其实就是多了一步指针的操作,在虚继承中,如果是通过对象实例来操作虚继承而来的data member,则不会有任何性能问题,因为不存在什么多态性,所有东西在编译的时候内存地址都确定了。

虚继承还是虚方法为了实现多态一样,多了一步,如果不需要多态,而是通过对象实例调用相关的方法就不会有性能问题。

C++ Data Member内存布局的更多相关文章

  1. 图说C++对象模型:对象内存布局详解

    0.前言 文章较长,而且内容相对来说比较枯燥,希望对C++对象的内存布局.虚表指针.虚基类指针等有深入了解的朋友可以慢慢看. 本文的结论都在VS2013上得到验证.不同的编译器在内存布局的细节上可能有 ...

  2. c++ 对象内存布局详解

    今天看了的,感觉需要了解对象内存的问题.参考:http://blog.jobbole.com/101583/ 1.何为C++对象模型? 引用<深度探索C++对象模型>这本书中的话: 有两个 ...

  3. Linux Debugging(四): 使用GDB来理解C++ 对象的内存布局(多重继承,虚继承)

    前一段时间再次拜读<Inside the C++ Object Model> 深入探索C++对象模型,有了进一步的理解,因此我也写了四篇博文算是读书笔记: Program Transfor ...

  4. 好文章系列C/C++——图说C++对象模型:对象内存布局详解

    注:收藏好文章,得出自己的笔记,以查漏补缺!     ------>原文链接:http://blog.jobbole.com/101583/ 前言 本文可加深对C++对象的内存布局.虚表指针.虚 ...

  5. 【转载】图说C++对象模型:对象内存布局详解

    原文: 图说C++对象模型:对象内存布局详解 正文 回到顶部 0.前言 文章较长,而且内容相对来说比较枯燥,希望对C++对象的内存布局.虚表指针.虚基类指针等有深入了解的朋友可以慢慢看.本文的结论都在 ...

  6. c++对象模型和对象内存布局

    简单对象模型:如下图所示: 每一个object是一系列的slots,每一个data member或者function member都有自己的一个slot.这种模型的空间和执行效率都很低.在这个模型中, ...

  7. C++ 系列:内存布局

    转载自http://www.cnblogs.com/skynet/archive/2011/03/07/1975479.html 为什么需要知道C/C++的内存布局和在哪可以可以找到想要的数据?知道内 ...

  8. 根据内存布局定位的一个fastdfs坑

    在使用fastdfs时,编写数据上传代码时,遇到一个坑.最终根据指针对应的内存布局定位到一个其client API的一个坑,值得记录一下.具体是在 tracker_connect_server() 这 ...

  9. Linux 内存布局

         本文主要简介在X86体系结构下和在ARM体系结构下,Linux内存布局的概况,力求简单明了,不过多深入概念,多以图示的方式来记忆理解,一图胜万言. Technorati 标签: 内存 布局 ...

随机推荐

  1. freeCodeCamp:Factorialize a Number

    计算一个整数的阶乘 如果用字母n来代表一个整数,阶乘代表着所有小于或等于n的整数的乘积. 阶乘通常简写成 n! 例如: 5! = 1 * 2 * 3 * 4 * 5 = 120 /*思路 阶乘等于fo ...

  2. c# 调用分页(控制器端的)与时间的格式

    1.在操作的model中: 2.在控制器中: , command.PageSize);//将从数据库获取到的集合进行分页 var gridModel = new DataSourceResult { ...

  3. Linux内核实现中断和中断处理(一)

    Linux实现中断处理 内核是怎么知道应用程序要调用系统调用的呢?或者说应用程序怎么通知系统内核自己需要执行一个系统调用,这是通过软中断实现的,通过引发一个异常来促使系统切换到内核态去执行异常处理程序 ...

  4. N皇后

    #include <stdio.h> #include <math.h> ], a[]; /*int place(int k) { for(int i = 1; i < ...

  5. img图片放大控件 lightbox.js

    1.引入相关js,css jquery-1.7.2.min.js,lightbox.css,lightbox.js   2 图片增加<a href="大图片地址" rel=& ...

  6. MyBatis框架——mybatis插入数据返回主键(mysql、oracle)

    向数据库中插入数据时,大多数情况都会使用自增列或者UUID做为主键.主键的值都是插入之前无法知道的,但很多情况下我们在插入数据后需要使用刚刚插入数据的主键,比如向两张关联表A.B中插入数据(A的主键是 ...

  7. python 之 theano学习:

    (1)theano主要支持符号矩阵表达式 (2)theano与numpy中都有broadcasting:numpy中是动态的,而theano需要在这之前就知道是哪维需要被广播.针对不同类型的数据给出如 ...

  8. 搭建Openstack云平台

    实验室需要做一个大数据平台项目,临时接下需要部署实验室云平台的任务,由于之前没有接触过相关技术,仅以此篇作为纪录文,记录一下我的openstack的初步学习以及搭建过程. 1.openstcak及其组 ...

  9. 解决 yum安装时报错 Error: Protected multilib versions: 报错

    系统中缺少一个lib库 libz.so.1文件,使用yum安装会自动找到相关的rpm包,如下命令 # yum -y install libz.so.1 Resolving Dependencies-- ...

  10. C#设计模式——抽象工厂

    一.引言 我相信看到这段文字的人,都具备了良好的技术功底.但是对于自己编写的代码总是充满抱怨,希望能够将自己编写的代码如同房子一般先进行有效 的设计,然后在进行建设.那么这篇文章能够给你一些思路,这里 ...