0x1、知识点
1、一个类对象所占据的内存空间由它的数据成员(非静态成员)所占据的数据空间总和决定(需要考虑对齐);
2、类的成员函数(包括静态、非静态)、虚函数本身不占据对象的内存空间;
3、空类、单一继承的空类、多继承的空类所占用空间大小为 1 ;
1.1、基本数据类型大小:
bool | 1个字节 |
char | 1个字节 |
short | 2个字节 |
int | 4个字节 |
long | 4个字节 |
float | 4个字节 |
double | 8个字节 |
0x2、详细介绍
2.1、普通类,无继承
先看代码:
class A {
public:
A() {
}
private:
int a;
int b;
char c;
int d;
char e[1];
};
int main()
{
A a;
size_t aa = sizeof(a);
printf("aa = %d\n", aa);
return 0;
}
上述代码类A中有五个私有成员变量,按照内存大小对齐来计算:int a --> 4字节
int b --> 4字节
char c --> 4字节
int d --> 4字节
char e --> 4字节
大小总计:20 字节
可能有人会有这个疑问:这个对齐是按照什么对齐的呢?答案就是按照成员变量中类型大小最大的来计算,即如果是int那么按照 4 字节对齐,如果是double类型的变量则是按照 8 字节对齐。而且需要注意的是,不要以为只有单字节的char在最后,且后面没有了其他数据成员就不用对齐了,依然是要对齐的!
上面的计算是否很简单?现在我们再来看成员变量中有double类型的类大小如何计算:
class A {
public:
A() {
}
private:
int a;
int b;
char c;
double d;
char e[1];
int f;
char g;
int h;
};
int main()
{
A a;
size_t aa = sizeof(a);
printf("aa = %d\n", aa);
return 0;
}
上述代码类A中有八个私有成员变量,按照内存大小对齐来计算:
int a --> 4个字节
int b --> 4个字节
char c --> 8个字节
double d --> 8个字节
char e --> 4个字节
int f --> 4个字节
char g --> 4个字节
int h --> 4个字节
大小总计:40 字节
大家看到,同样是 char 类型的数据,凭什么 "char c"就是占 8 个字节,而 "char e" 和 "char g" 就只占 4 个字节? 这是为什么呢?如之前所说的,对齐值是按照最大的数据类型大小来算的,至于为什么 "char c" 是8个字节而 "char e" 和 "char g" 是4字节的原因是因为 "char e" 4 字节对齐后加上 "int f" 4个字节后刚刚凑够 8 个字节了,他们俩搭伙凑够 8 字节也正符合当前 8 字节对齐的规律。而 "char c" 很孤单,没人和它搭伙,所以就自己凑出 8 字节咯!2.2、普通类,有继承
如果只是单纯的普通类,且没有虚函数和虚继承的话,那么子类会继承父类所有成员。但是虽然继承了,如果父类中的成员是私有的话是无法被访问的。所以在这种情况下的继承,子类的大小就是父类的大小 + 子类成员大小,而且子类中同样也继承了父类中的对齐规则,8 字节对齐的依然还是 8 字节对齐。
class A {
public:
A() {
}
private:
int a;
int b;
char c;
double d;
char e[1];
int f;
};
class B :public A {
private:
char a;
int b;
};
int main()
{
A a;
size_t aa = sizeof(a);
B b;
size_t bb = sizeof(b);
printf("aa = %d\nbb = %d\n", aa, bb);
return 0;
父类 A 的大小:int a --> 4 字节
int b --> 4 字节
char c --> 8 字节
double d --> 8 字节
char e --> 4 字节
int f --> 4 字节
大小总计:32 字节
子类 B 的大小:
char a --> 4 字节
int b --> 4 字节
父类 A --> 32 字节
大小总计:40 字节
2.3、有虚函数的类,有继承
有虚函数的类,除了自身所有成员变量的大小外,还要加上由于有虚函数而被编译器生成的虚函数表指针,这个指针的大小同样受最大类型对齐影响,而且这个虚函数表指针的大小是在所有成员大小计算完后加上的,所以如果是最大类型为 int 的,则是所有成员大小之和之上再加 4 。同理,如果最大类型是double,则是加 8 。class A {
public:
A() {
}
virtual void fun() {}
private:
int a;
int b;
char c;
double d;
char e[1];
int f;
};
class B :public A {
public:
virtual void fun1() {}
private:
char g;
int h;
};
int main()
{
A a;
size_t aa = sizeof(a);
B b;
size_t bb = sizeof(b);
printf("aa = %d\nbb = %d\n", aa, bb);
return 0;
}
父类 A 的大小:
int a --> 4 字节
int b --> 4 字节
char c --> 8 字节
double d --> 8 字节
char e --> 4 字节
int f --> 4 字节
虚函数表指针大小 --> 8 字节
大小总计:40 字节
子类 B 的大小:
char g --> 4 字节
int h --> 4 字节
父类 A 的大小 --> 40 字节
大小总计: 48 字节
需要注意的是:虽然子类也有自己的虚函数,但是它继承了父类的虚函数表指针,也等于继承了父类的虚函数表。除非父类中没有虚函数,那么子类是不会自己再生成一个虚函数表的。它自己的虚函数地址会加入到从父类继承过来的虚函数表中。
0x3、最后
还有关于虚继承的内容之后再添加上,关于《C++类对象占用空间大小计算》到此就结束了。本人才疏学浅,文中内容虽经过VS2015编译测试,但也可能难免疏忽出错,如果看官发现了错误还请及时留言指正!谢谢!
如果您觉得这篇文章对您有所帮助,还请给个点赞鼓励!谢谢!