c++类的内存布局

 

by andydeng • 2011 年 4 月 3 日 • C++1 Comment

本文基本上是对于Stanley B.Lippman的Inside The C++ Object Model一书第一章第三章的概括,描述了c++类的内存布局情况.

c++的类的内存布局有如下规则:
1. Nonstatic data member 存放在Class Object中;
2. Static data member, static/nonstatic member function存放在class object之外.
3. 若类有virtural function, 则在class object 中增加virtual pointer(vptr)指向virtural function tabel(vtbl). vptr在类中的位置有两种情况:1是在所有成员变量之后,这么做的好处是这个类能和c语言兼容.2是在最前面,这样做的话,虚拟继承等实现会方便一点,但是不能和c兼容.在前或后依赖编译器实现.
4. nonstatic data member 若在同一个access section中, 则变量在内存中的顺序保证和声明中的顺序一致(较晚出现的变量有较高的地址).而不同access section中的数据顺序则没有保证,依赖编译器实现.
5. 内存对齐: 类的各个成员,第一个成员位于offset为0的位置,以后每个数据成员的偏移量必须是min(#pragma pack(), 这个数据成员自身的长度)的倍数
             数据成员自身对齐后, 类本身也要进行对齐, 对齐将按照min(#pragma pack(), 类中长度最大的成员)的倍数进行.
6. 如果类为空,编译器会安插1byte的数据到类中,以确保类的每个实例都会有唯一的内存地址
7. 继承后,子类的数据成员不会占用父类内存对齐用的空间. C++语言保证:"出现在derivd class 中的base class subobject有其完整的原样性"
8. 如果类的继承体系不是单一,而是多重继承,但是不含虚拟继承,那么有多少条继承链,内存布局中就有多少个vptr.多个继承链的位置,和继承时的声明顺序一致.
9. 如果使用了虚拟继承,则先将derived class的不变部分布局,然后再布局虚拟继承的base class,而具体布局则有以下情况:
  1. 使用Pointer Strategy, 每一个虚拟继承的类,都有一个额外的指针指向base class
  2. 使用Virtual table offset strategy, 不加入额外的指针指向base class,而是在vtbl的-1的offset内放置该类与虚拟继承的基类之间的offset. 这样的话运行时则可以通过derivedPointer + vptr[-1]得到.
  3. 使用Virtual base class table. 这个是微软的做法,不过书中并没有具体描述怎么做,根据我的理解好像是在vtbl中加入一个指针指向base class.
如果采用2或3, 那么内存布局和8并不会有太大区别,就是virtual base class跑到最后面去了.
第9种情况异常复杂,建议看原书外加自己在多个编译器上实践实践.

用实例来说说(32位机器)
规则1和2:
class A
{
public:
int a;
static char b;
void foo();
static void bar();
};
ASSERT(sizeof(A) == 4);
那么在每一个A对象内,只含有一个a,也就是,sizeof(A) == 4 .
而b,foo(),bar(),都不在class object之内,他们在内存中有唯一实体.

规则3
class B
{
public:
int a;
virtual void foo();
};
ASSERT(sizeof(B) == 8);
一个int和一个vptr,共8位

规则4,没啥好说的,一般就算在不同的access section,都会按照一致的顺序来声明.但是要注意顺序一致不代表连续.因为变量间可能会有一些bytes用于内存对齐.

规则5
class C
{
int a;
char b;
};
ASSERT(sizeof(C) == 8);
规则5有两条子规则,第一条对这个例子没用,经过第一条后C的大小还是5,可是第二条要求整个类要对齐,那么必须在char b后增加3bytes.
如果int和char的声明顺序反一下,那么为满足第一条规则,类已经需要对齐成8了,已经是8那么第二条也满足了.

规则6
class D
{};
ASSERT(sizeof(D) == 1);
这1byte是编译器插进去的,如果不插的话,连续声明D a,b;再取他们的地址,就会变成一样的了.就无法分辨哪个变量是哪个了.
不过要注意的是,任何类继承了D,只要里面有vptr或者任何一个变量,那么编译器就不会在子类中加入这1 byte了.(这个是依赖于编译器的,而不是标准规定.如果编译器没有去掉这1byte的话,那么就要内存对齐了.)

规则7
class E:public C
{
int c;
char d[2];
};
ASSERT(sizeof(E) == 16);
编译器不会为了节省空间把E的成员插入到C为了内存对齐的而补出的空间中的.这道题我面试的时候被问过,我答16的时候面试官还认为错了,太浪费空间了.但是这的确是唯一的正确解.

规则8
class F
{
int b;
virtual void bar();
};
class G:public B, public F
{
int c;
};
ASSERT(sizeof(G) == 20);
3个int,2个vptr,一共20

规则9
class H:virtual public B
{
int a;
};
class I:virtual public B
{
int b;
};
class J:public H, public I
{
int a;
};
ASSERT(sizeof(J) == 28);
4个int一共是16,BHI3个类都有各自的vptr,16+4*3==28.

这东西其实我一年半前就看书看到过,可惜实际编程中基本是用不到这种东西的,导致我忘了不少,面试的时候有几个没答出来,十分可惜,特意花一天时间重新啃了那书再总结总结加深记忆.另外真的要对自己说声加油啊.

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/353660.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

matlab 五点三次平滑算法

(2012-04-23 21:01:31) 转载▼标签: 杂谈 分类: matlab http://www.ilovematlab.cn/thread-71818-1-1.html 这里提供一个函数mean5_3(五点三次平滑算法)对数据进行平滑处理: load V1.mat subplot 211; plot(V1); ylim([2000 7000]); grid; y…

spring配置xml文件_XML配置文件中的Spring配置文件

spring配置xml文件我的上一个博客非常简单,因为它涵盖了我从Spring 3.0.x到Spring 3.1.x的轻松升级,最后我提到可以将Spring模式升级到3.1,以利用Spring的最新功能。 在今天的博客中,我将介绍这些功能中最酷的功能之一&#xff1a…

数组指针和指针数组的区别

数组指针(也称行指针) 定义 int (*p)[n]; ()优先级高,首先说明p是一个指针,指向一个整型的一维数组,这个一维数组的长度是n,也可以说是p的步长。也就是说执行p1时,p要跨过n个整型数据的长度。 如…

用JS写的取存款功能

console.log("请输入用户名:");let username readline.question(); // 接收用户输入的用户名console.log("请输入密码:");let password readline.question(); // 接收用户输入的密码let arr [["123", "123…

您在2016年OpenStack峰会上错过的事情

今年我第一次参加了4月25日至29日在德克萨斯州奥斯汀举行的OpenStack峰会。 今天结束了,我要回家了,我想回顾一下,从我的角度分享你错过的事情。 作为以应用程序开发人员为重点的技术传播者,转移到包含Red Hat产品组合的基础架构…

HDU1069 最长上升子序列

emm。。。。矩形嵌套 还记得吗。。。。就是它。。。 直接贴代码了。。。。 import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.Scanner;public class Main{final static int maxn 1000000;…

linux 中配置假域名来测试

1.linux中配置假域名 找到hosts文件进行编辑 命令:vim /etc/hosts 配置: #centos(本机IP)192.168.1.179 www.imooc.com(假域名,自己设置)192.168.1.179 image.imooc.com192.168.1.179 s.imooc.c…

C/C++中的常量指针与指针常量

常量指针 常量指针是指向常量的指针,指针指向的内存地址的内容是不可修改的。 常量指针定义“const int *p&a;”告诉编译器,*p是常量,不能将*p作为左值进行操作。但这里的指针p还是一个变量,它的内容存放常量的地址&#xff0…

基于javafx的五子棋_JavaFX中基于表达式的PathTransitions

基于javafx的五子棋在JavaFX中,您可以使用PathTransition对象为路径上的节点设置动画。 PathTransitions使用Shape对象来描述它们需要沿其动画的路径。 JavaFX提供了各种类型的形状(例如,多边形,圆形,多边形&#xff0…

ES6三种暴露方法

1.多行暴露(分行暴露) 导出 //test.js export function test1(){console.log(测试分别导出test1); } export function test2(){console.log(测试分别导出test2); } 导入: //index.js import {test1, test2} from ./test.js //文件路径二&…

shell获取当前执行脚本的路径

filepath$(cd "$(dirname "$0")"; pwd)脚本文件的绝对路径存在了环境变量filepath中,可以用echo $filepath查看完整路径在shell中:$0: 获取当前脚本的名称$#: 传递给脚本的参数个数$$: shell脚本的进程号$1, $2, $3...:脚…

MANIFEST.MF文件详解

参考百度百科的解释如下: http://baike.baidu.com/item/MANIFEST.MF MANIFEST.MF:这个 manifest 文件定义了与扩展和包相关的数据。单词“manifest”的意思是“显示” ### MANIFEST.MF文件介绍------------------------------ 主要包含3个部分 - Manifes…

Drools 6.4.0.Final提供

最新和最出色的Drools 6.4.0.Final版本现已可供下载。 这是我们先前构建的增量版本,对核心引擎和Web工作台进行了一些改进。 您可以在此处找到更多详细信息,下载和文档: Drools网站 资料下载 文献资料 发行说明 请阅读下面的一些发行要…

Linux Shell中各种分号和括号的用法总结

[日期:2011-02-21] 来源:Linux社区 作者:破烂熊 [字体:大 中 小] 各种括号的用法总结如下 1.Shell中变量的原形:${var} 大家常见的变量形式都是$var 2.命令替换$(cmd) 命令替换$(cmd)和符号cmd(注意这不是单引号…

软考解析:2014年下半年下午试题

软考解析:2014年下半年下午试题 第一题:数据流图 第四题:算法题 第五题:Java设计模式 转载于:https://www.cnblogs.com/MrSaver/p/9073778.html

malloc()参数为0的情况

问题来自于《程序员面试宝典(第三版)》第12.2节问题9(这里不评价《程序员面试宝典》,就题论题): 下面的代码片段输出是什么?为什么? char *ptr;if((ptr (char *)malloc(0))NULL)put…

铁乐学python_Day42_锁和队列

铁乐学python_Day42_锁和队列 例:多个线程抢占资源的情况 from threading import Thread import timedef work():global ntemp ntime.sleep(0.1)n temp - 1if __name__ __main__:n 100l []for i in range(100):p Thread(targetwork)l.append(p)p.start()for p…

hibernate 懒加载_Hibernate懒/急加载示例

hibernate 懒加载这篇文章将重点讨论为什么以及如何在应用程序中使用称为LAZY和EAGER加载的概念,以及如何使用Spring的Hibernate模板以EAGER方式加载LAZY实体。 当然,正如标题本身所暗示的那样,我们将通过一个示例来说明这一点。 场景就是这样…

C++ 关键字typeid

转载网址:http://www.cppblog.com/smagle/archive/2010/05/14/115286.aspx 在揭开typeid神秘面纱之前,我们先来了解一下RTTI(Run-Time Type Identification,运行时类型识别),它使程序能够获取由基指针或引用…

使用Apache Camel进行负载平衡

在此示例中,我们将向您展示如何使用Apache Camel作为系统的负载平衡器。 在计算机世界中,负载均衡器是一种充当反向代理并在许多服务器之间分配网络或应用程序流量的设备。 负载平衡器用于增加容量(并发用户)和应用程序的可靠性。…