C++对象的内存大小由以下几个方面决定:
- 空类的大小默认为1
- 类中非静态成员的总和
- 如果有继承,派生类的对象会加上基类对象的数据成员
- 如果有virtual,那么就需要加上一个虚函数表指针的大小(在继承方式下,父子类共享一个虚函数表,因此子类的虚函数表指针是继承于父类的,不会新增一个虚函数表指针)
- 内存对齐补齐规则
1. 无继承下的对象内存模型
案例:
class ZooAnimal {
public:
ZooAnimal()=default;
virtual ~ZooAnimal() {}
virtual void rotate() {}
protected:
int loc;
std::string name;
};
int main()
{
std::string s;
std::cout << sizeof(s) << std::endl;
ZooAnimal za;
ZooAnimal *pza = &za;
std::cout << "sizeof ZooAnimal is " << sizeof(za) << std::endl;
return 0;
}
演示结果:
- 下面在32位编译器下运行(指针大小为4字节,string的大小为28字节)64位编译器下string为40个字节。
- 因此ZooAnimal类对象的大小为:string(28字节)+int(4字节)+虚函数表指针(4字节)=36字节
其内存模型如下:
- 假设pza指针所指的地址为1000,其指向的对象模型如下所示
- __vptr为虚函数表指针
2. 有继承关系的对象内存模型
派生类的内存空间中会包含基类的内存成员,并且派生类会将自己的新数据成员新增在自己的内存模型中。
演示案例:
- 如果我们有一个类继承于上面的那个ZooAnimal
class Bear :public ZooAnimal {
public:
Bear() {}
~Bear() {}
void rotate() {}
virtual void dance() {}
protected:
enum Dances {};
Dances dances_known;
int cell_block;
};
int main()
{
std::string s;
std::cout << sizeof(s) << std::endl;
ZooAnimal za;
ZooAnimal *pza = &za;
std::cout << "sizeof ZooAnimal is " << sizeof(za) << std::endl;
Bear b;
Bear *pb = &b;
Bear &rb = *pb;
std::cout << "sizeof ZooAnimal is " << sizeof(b) << std::endl;
return 0;
}
演示结果:
- 下面在32位编译器下运行(指针大小为4字节,string的大小为28字节)
- 因此Bear类对象的大小为:string(28字节)+int(4字节)+虚函数表指针(4字节)+dances_known(4字节)+cell_block(4字节)=44
其内存模型如下:
- 假设pb指针的所指的地址为1000,其指向的对象模型如下所示
- __vptr为虚函数表指针
3. 虚继承下的内存模型
演示案例
#include<stdio.h>
class X{};
class Y :virtual public X {};
class Z :virtual public X {};
class A :public Y, public Z {};
int main()
{
X x;
printf("sizeof x is %d\n", sizeof(x));
Y y;
printf("sizeof y is %d\n", sizeof(y));
Z z;
printf("sizeof z is %d\n", sizeof(z));
A a;
printf("sizeof a is %d\n", sizeof(a));
return 0;
}
- x:空类的大小为1
- y:有虚函数表指针,指针为4字节(32位机器上)
- z:有虚函数表指针,指针为4字节(32位机器上)
- a:两个虚函数表指针,一个指向于y,一个指向于z
如果我们把X改为以下形式,则:
- x:a为int,因此为4
- y:一个虚函数表指针(4字节)+a的大小(4字节)
- z:一个虚函数表指针(4字节)+a的大小(4字节)
- a:两个虚函数表指针(8字节,一个指向于y,一个指向于z)+a的大小(4字节,因为virtual继承只会保存一份基类的实例,所以只有一个a)
class X
{
int a;
};