当前位置:   article > 正文

C++ 多重继承的内存布局和指针偏移

C++ 多重继承的内存布局和指针偏移

在 C++ 程序里,在有多重继承的类里面。指向派生类对象的基类指针,其实是指向了派生类对象里面,该基类对象的起始位置,该位置相对于派生类对象可能有偏移。偏移的大小,等于派生类的继承顺序表里面,排在该类前面的所有的类的数据成员(含虚表指针)所占的空间大小总和。

下面以一个简单的程序为例,揭示有多重继承关系的派生类对象的内存布局:

  1. #include <stdio.h>
  2. #pragma pack(4)
  3. class A {
  4. void foo() {}
  5. };
  6. class B {
  7. public:
  8. virtual void func() {};
  9. private:
  10. int b;
  11. };
  12. class C {
  13. int c;
  14. };
  15. class D : public A, public B, public C {
  16. public:
  17. void func() override {};
  18. private:
  19. int d;
  20. };
  21. int main(int argc, const char* argv[]) {
  22. D* pd = new D();
  23. A* pa = pd;
  24. B* pb = pd;
  25. C* pc = pd;
  26. printf("pd=%p, pa=%p, pb=%p, pc=%p\n"
  27. "sizeof(A)=%zd\n"
  28. "sizeof(B)=%zd\n"
  29. "sizeof(C)=%zd\n"
  30. "sizeof(D)=%zd\n",
  31. pd, pa, pb, pc,
  32. sizeof(A), sizeof(B),
  33. sizeof(C), sizeof(D));
  34. return 0;
  35. }

注意该程序用 #pragma pack 指令指示数据在内存中按 4 字节来对齐。在 x64 平台上编译执行结果:

  1. $ g++ test.cc -std=c++11
  2. $ ./a.out
  3. pd=0x10c0010, pa=0x10c0010, pb=0x10c0010, pc=0x10c001c
  4. sizeof(A)=1
  5. sizeof(B)=12
  6. sizeof(C)=4
  7. sizeof(D)=20

我们首先来分析这四个类的大小。

A 只有一个普通函数成员 foo,没有任何数据成员,是一个空类,其大小为 1 字节。之所以空类大小不为零,是需要标识类对象在内存中的位置,这 1 字节空间仅作占位用,不代表任何意义。

B 有一个成员变量 int b 和一个虚函数成员 func,其中 b 的大小为 4 字节。 由于存在虚函数,因此 B 类起始位置有一个虚表指针(vptr),在 64 平台上指针的大小为 8 字节。因此 B 的大小为 4 + 8 = 12 字节。

C 仅有一个成员变量 int c,因此其大小也就为 4 字节。

D 继承自A, B, C,它的大小等于 A, B, C 的所有数据成员的大小,加上其自身的数据成员和虚表指针的总的大小:4(b) + 4(c) + 4(d) + 8(vptr) = 20 字节。

注意:在 D 的继承关系链里面,只有基类 B 有虚函数,因此对于 D 对象而言,总体只有一个虚表指针,也就是(B)基类对象中的虚表指针。如果派生类的多个基类都有虚函数,则对应每个有虚函数的基类,在派生类对象里都有一个虚表指针。

因此,对于分析派生类 D 的对象,其内存布局如下:

分析结果与程序运行结果一致。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/不正经/article/detail/670810
推荐阅读
相关标签
  

闽ICP备14008679号