C++对象的内存模型
1. 普通对象模型
对象是如何在内存中布局的?
成员 | 存放位置 | 访问范围 |
---|---|---|
非静态数据成员 | 每一个对象体内 | 为该对象专有 |
静态数据成员 | 程序的静态存储区内,只有一份实体 | 为该类所有对象共享 |
成员函数(静态/非静态) | 程序的代码段中,只有一份实体 | 为该类所有对象共享 |
类类嵌套的各种类型(typedef、class、struct、enum等) | 与放在类外面定义的类型除了定义域之外没有本质区别 | 遵循类内部的访问规则 |
因此,构成对象本身的只有数据,类的成员函数不属于任何一个对象。非静态成员函数与对象之间的关系是通过this指针绑定的。
2. 增加继承和虚函数的类的对象模型
派生类的对象是如何在内存中布局的?
成员 | 规则 |
---|---|
继承自基类的非静态数据成员 | 作为对象自己专用的数据 |
继承自基类的非静态成员函数 | 作为类的成员函数一样访问 |
虚函数(继承的或者新增的) | 所有虚函数的地址都存放在vtable里 |
多态类每一个对象中的vptr | 函数指针的指针,指向所属类的vtable |
继承自基类的vptr | 继承和重用该vptr |
多重继承的多个分支 | 每个分支都继承一个vptr并生成一个对应的vtable |
vptr在派生类对象中的相对位置不会随着继承的层次改变而改变,一般编译器会将vptr放在所有数据成员的最前面。
为了支持RTTI,编译器会为每一个多态类创建一个type_info对象,并把其地址保存在该类vtable中的第一个位置。
3. vtable中虚函数指针的排列顺序
- 如果虚函数是第一次出现,则把它的函数地址指针依次插入到vtable的尾部。
- 如果派生类改写了基类的虚函数,则这个函数的地址在派生类vtable中的位置与其在基类vtable中的位置一直,而与其在派生类中的声明位置无关。也就是说一旦虚函数指针在vtable的位置确定,则它的位置就不会因为继承层次的改变而改变。
- 派生类没有改写的基类虚函数被继承,位置与原先在vtable中的位置相同。