在C++中的某一个类的内存大小是由以下三部分组成:
1:nonstatic data member的总和大小
2:alignment所填补的空间,可能存在于members之间,也可能是类的边界
3:为了支持virtual而由内部产生的额外负担,比如:指虚基类表的指针或指向虚函数表的指针
其中1和3都比较好确定,比较难确定的是2中的内存对齐(填补)的大小
以下的分析都是Visual Stdio编译器进行分析
alignment在类中对齐规则是:
一、先按照自身的数据类型进行对齐,再按照整个类的数据类型进行对齐。内存对齐中自身数据类型对齐很重要的一点是:自身的数据类型大小是多少,则存储地址的起始位置则是它的倍数!比如:int大小是4字节,则存储Int类型的起始内存位置必须是4的倍数,0x0000,0x0008,0x0016等
二、自身数据类型就是类型本身的大小(也可以为是类型本身大小的倍数),比如int就是4,char就是1。。。。
三、整个类的数据类型对齐大小为类中非静态数据成员中类型最大的大小,比如
class A
{
char ch;
int a;
double d;
}
则A类的对齐大小为8。
例子分析:
以上述A类为例
ch在0x0000位置上,a为int类型,它的起始内存位置要为4的倍数,所以应该为0x0004,于是0x0001,0x0002,0x0003三个字节就被填充了。最后a占了0x0004,0x0005,0x0006,0x0007。在要占据8个字节,所以内存的起始位置应该是0x0008,刚好符合。前面是按照自身数据类型对齐,后面要按照整个类的数据类型对齐,而类的数据类型为8,A类占据了0x0000-0x00015。总共大小为16,刚好是8的倍数,所以整个A类内存对齐完毕。所以sizeof(A)=16;
class B
{
int a;
double d;
char ch;
}
跟上面一样分析,a占据了4个字节,为了满足d内存起始位置是8的倍数,所以a后面填补了4字节,加上d的8字节,最后ch大小为1字节,可以直接存入,总共为4(a的大小)+4(填补)+8(d的大小)+1(ch的大小)=17。最后整个数据类型对齐是8的倍数,所以17+7=24;所以sizeof(B)=24。
四、总结:A和B数据成员相同,仅仅是因为成员顺序不同,所以整个内存大小就不同。原因是因为内存对齐。
补充:1:指针(包括指向虚函数表的指针vfptr和指向虚类表的指针vbptr)是单独与
整个类的数据进行对齐(与类成员变量中字节最大的成员的内存大小对齐)。若类的数据对齐大小是小于等于4的,就直接是4,
因为vfptr大小是4。比如:class A{char ch;int a;double d;}则vfptr
应该与d的内存大小8字节对齐;class A{char ch;}则vfptr直接与自身大小4对齐。
且vfptr是在类内存的非静态数据成员的前面。
2:在单一的非虚继承中,vfptr都是来自于基类!!所以vfptr只指向虚函数表中基类的部分
!若重写虚函数,则覆盖掉虚函数表里基类部分对应的虚函数地址。这就实现了多态
且基类指针不能调用属于派生自己的虚函数
3:在虚继承中,若派生类的虚函数有不是重写基类的虚函数,则派生类中有自己的vfptr,不从基类中继承
;若全是重写基类的虚函数则派生类的vfptr从基类中继承