小米企业网站的优化建议,网站建设哪家便宜,网站备案 个人 单位,做一个好的公司网站有什么好处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 membe…
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,164*328. 这东西其实我一年半前就看书看到过,可惜实际编程中基本是用不到这种东西的,导致我忘了不少,面试的时候有几个没答出来,十分可惜,特意花一天时间重新啃了那书再总结总结加深记忆.另外真的要对自己说声加油啊.