sizeof 大小计算
首先,sizeof是一种单目运算符,他并不是函数,在编译器就可以得到类的大小是多少。
这个里面主要的问题就是类的大小的计算过程:
首先,对于空类来说 ,sizeof 是1。因为每个类都需要在内存中有一个唯一的标识,所以他必须需要占用一定的大小。并且cpp标准里面规定了,sizeof的结果必须是非0值。
其次就是重叠对象的问题。如果这个空对象作为一个类的成员,那么他就是char一样,需要占用一定的内存
#include <iostream> struct Empty {}; struct ContainsEmpty { Empty e; int i; }; int main() { std::cout << "sizeof(Empty): " << sizeof(Empty) << std::endl; std::cout << "sizeof(int): " << sizeof(int) << std::endl; std::cout << "sizeof(ContainsEmpty): " << sizeof(ContainsEmpty) << std::endl; return 0; }
2. ```cpp sizeof(Empty): 1 sizeof(int): 4 sizeof(ContainsEmpty): 8
如果是空类被继承的话,这个时候计算的就会忽略父类的大小
#include <iostream> struct Empty {}; struct ContainsEmpty:public Empty { int i; // Empty e; }; int main() { std::cout << "sizeof(Empty): " << sizeof(Empty) << std::endl; std::cout << "sizeof(int): " << sizeof(int) << std::endl; std::cout << "sizeof(ContainsEmpty): " << sizeof(ContainsEmpty) << std::endl; return 0; }
```cpp sizeof(Empty): 1 sizeof(int): 4 sizeof(ContainsEmpty): 4
然后就是普通类的计算法则
类的大小必须要是类中成员最大值的整数倍。这个主要考虑的点是数组的情况,比如在数组情况下,我们申请了多个相邻的对象,如果没有添加补充,不是成员最大值的整数倍,那么在数组情况下最终还是会发生偏移。
- 这片文章里面详细说明了
每个成员的起始地址都要是自己的整数倍才可以。这个比较容易理解,主要是内存对其的好处,不会跨cache line访问。
有了这两个规则,普通的,平凡类型的类我们就可以计算了。
接下来就是包含虚函数的问题了
- 虚函数的实现,需要虚函数指针。64位下占用8字节。每个基类都要占用一个。
- 他是一层一层叠的,按照书写的顺序,第一个类的虚函数指针,然后第一个类的成员。注意对齐。然后是第二各类的虚函数指针,然后是第二各类的数据成员
- 这个时候也就需要遵守普通的类的规范,也就是说要各种对齐
最后也就是虚基类的事情了
虚基类是解决菱形继承的问题
他的数据成员的布局是这样的,首先是普通基类的数据布局,普通基类的数据成员按照上面说的逐个排列。
在最后的地方,存放虚基类。然后问题就是我们怎么找到虚基类。无论是我们的子类,还是说上层的基类,都需要相应的索引找到对应的虚基类。
在数据成员开始的地方,我们需要一个虚基址表,用于这个类去找到虚基类的数据。这个虚表就是,他的作用就是方便对象去寻找自己的虚基类成员。
这里还会有一些拓展,就是说如果伴随着虚基类,这个可能会更加复杂。
- 如果主基类里面也有虚函数,那么第一个虚指针会服用。
- 随后就是按照场里,每个基类都有一个虚指针
- 如果虚基类里面也有虚函数,那么在虚基类上面也会有虚指针。