我试图理解多重继承中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
中间零的含义是什么? 提前谢谢!
答案 0 :(得分:2)
这取决于你的编译器。有了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
这种布局似乎很明智,对吧?正是你所期待的。 在程序中没有像在调试器中那样显示干净的原因是你要转储int大小的单词。在64位系统sizeof(int)== 4但sizeof(void *)== 8
因此,您会看到指针分为(int,int)对。在Linux上,你的指针没有任何位设置超过低32,在我的指针OSX上 - 因此0和1差异的原因
答案 1 :(得分:2)
这非常依赖于体系结构和编译器...可能对你来说,指针的大小可能不是int的大小......你使用什么架构/编译器?
答案 2 :(得分:2)
如果您正在使用64位系统,那么:
第一个零是第一个vfptr
中最重要的4个字节。
第二个零是填充,因此第二个vfptr
将与8字节地址对齐。
第三个零是第二个vfptr
中最重要的4个字节。
您可以检查是否sizeof(void*) == 8
以断言。
答案 3 :(得分:1)
很难说不知道你的平台和编译器,但这可能是一个对齐问题。实际上,编译器可能会尝试沿8字节边界对齐类数据,并将零用于填充。
如果没有上述细节,这只是推测。
答案 4 :(得分:1)
这完全取决于您的编译器,系统,位数。
虚拟表指针将具有指针的大小。这取决于您是将文件编译为32位还是64位。指针也将在其大小的多个地址处对齐(通常是任何类型)。这可能是您在20
之后看到0填充的原因。
整数将在特定系统上具有整数的大小。这通常是32位。请注意,如果您的计算机不是这种情况,您将得到意外的结果,因为您使用指针算法将size(int)增加了ptr。
答案 5 :(得分:1)
如果您使用MVSC,您可以使用-d1reportAllClassLayout转储解决方案中所有类的所有内存布局:
cl -d1reportAllClassLayout main.cpp
希望对你有所帮助