我正在尝试了解多重继承中C++的对象布局。为此,我有两个超类 A、B 和一个子类 C。尝试转储时,我期望看到的是:VFPTR |A 的字段 |VFPTR |B 的字段 |C的字段。
我得到了这个模型,但有一些我不明白的零。这是我正在尝试的代码
#include <iostream>
using namespace std;
class A{
public:
int a;
A(){ a = 5; }
virtual void foo(){ }
};
class B{
public:
int b;
B(){ b = 10; }
virtual void foo2(){ }
};
class C : public A, public B{
public:
int c;
C(){ c = 15; a = 20;}
virtual void foo2(){ cout << "Heeello!n";}
};
int main()
{
C c;
int *ptr;
ptr = (int *)&c;
cout << *ptr << endl;
ptr++;
cout << *ptr << endl;
ptr++;
cout << *ptr << endl;
ptr++;
cout << *ptr << endl;
ptr++;
cout << *ptr << endl;
ptr++;
cout << *ptr << endl;
ptr++;
cout << *ptr << endl;
ptr++;
cout << *ptr << endl;
ptr++;
cout << *ptr << endl;
return 0;
}
这是我得到的输出:
4198384 //vfptr
0
20 // value of a
0
4198416 //vfptr
0
10 // value of b
15 // value of c
中间的零是什么意思?提前感谢!
这取决于你的编译器。使用 clang-500,我得到:
191787296
1
20
0
191787328
1
10
15
1785512560
我确信也有 GDB 方法,但如果我在类对象的地址使用 LLDB 转储指针大小的单词,这就是我得到的:
0x7fff5fbff9d0: 0x0000000100002120 vtable for C + 16
0x7fff5fbff9d8: 0x0000000000000014
0x7fff5fbff9e0: 0x0000000100002140 vtable for C + 48
0x7fff5fbff9e8: 0x0000000f0000000a
这种布局似乎很合理,对吧?正是您所期望的。这在程序中显示不像在调试器中那样干净的原因是因为您正在转储整数大小的单词。在 64 位系统上,sizeof(int)==4 但 sizeof(void*)==8
因此,您会看到指针分成(int,int)对。在 Linux 上,您的指针没有设置低于 32 的任何位,在 OSX 上,我的指针设置了 - 因此 0 与 1 差异的原因
在很大程度上依赖于架构和编译器......可能对您来说,指针的大小可能不是整数的大小......您使用什么架构/编译器?
如果您使用的是 64 位系统,则:
- 第一个
零是第一个
vfptr
的 4 个最高有效字节。
第二个零是填充,以便第二个
vfptr
将与 8 字节地址对齐。第三个零是第二个
vfptr
的 4 个最高有效字节。
您可以检查是否sizeof(void*) == 8
以断言这一点。
如果不了解您的平台和编译器,很难分辨,但这可能是一个对齐问题。实际上,编译器可能会尝试沿 8 字节边界对齐类数据,其中零用于填充。
如果没有上述细节,这只是猜测。
这完全取决于你的编译器、系统、位数。
虚拟表指针将具有指针的大小。这取决于您是将文件编译为 32 位还是 64 位。指针也将在其大小的多个地址对齐(就像通常的任何类型一样)。这可能就是您在20
后看到 0 填充的原因。
整数将具有特定系统上的整数大小。这通常始终是 32 位。请注意,如果您的机器上不是这种情况,您将获得意外的结果,因为您正在使用指针算法通过 sizeof(int) 增加 ptr。
MVSC,则可以使用 -d1reportAllClassLayout 转储解决方案中所有类的所有内存布局,如下所示:
cl -d1reportAllClassLayout main.cpp
希望对您有所帮助