- C++对象的内存大小由以下几个方面决定:
- 1.空类的大小默认为1
- 2.类中非静态成员的总和
- 3.如果有继承,派生类的对象会加上基类对象的数据成员
- 4.如果有virtual,那么就需要加上一个虚函数表指针的大小(在继承方式下,父子类共享一个虚函数表,因此子类的虚函数表指针是继承于父类的,不会新增一个虚函数表指针)
- 5.内存对齐补齐规则
一、无继承下的对象内存模型
演示案例
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字节)
- 因此ZooAnimal类对象的大小为:string(28字节)+int(4字节)+虚函数表指针(4字节)=36字节
- 其内存模型如下:
- 假设pza指针所指的地址为1000,其指向的对象模型如下所示
- __vptr为虚函数表指针
二、有继承关系的对象内存模型
- 派生类的内存空间中会包含基类的内存成员,并且派生类会将自己的新数据成员新增在自己的内存模型中
演示案例
- 如果我们有一个类继承于上面的那个ZooAnimal
class Bear :public ZooAnimal { 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为虚函数表指针
三、虚继承下的内存模型
演示案例
#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; };