任何人都可以向我解释这里发生了什么吗?首先,我认为大多数程序员都知道具有虚函数的类具有vtbl,因此在其顶部有4个额外的字节。据我所知,这是相当标准的。我已经测试了这个,并利用这个事实,然后从带有修补vtbls的二进制文件加载到位。在过去的6个月里,我一直在Xcode工作,最近才遇到需要做一些加载的东西,所以我再次考虑修补vtbls。为了确保我的理解是正确的,我写了一个示例程序。这是:
class A
{
public:
virtual int getData()
{
return a;
}
virtual void print()
{
printf("hello\n");
}
int a;
};
class B : public A
{
public:
int getData()
{
return b;
}
int b;
};
class C : public B
{
public:
int getData()
{
return c;
}
void print()
{
printf("world\n");
}
int c;
};
class D
{
public:
int a;
int b;
};
int main (int argc, const char * argv[])
{
A* tA = new A();
tA->a = 1;
printf("A: %d\n", sizeof(A));
printf("A data: %d\n", tA->getData());
B* tB = new B();
tB->a = 2;
tB->b = 4;
printf("B: %d\n", sizeof(B));
printf("B data: %d\n", tB->getData());
C* tC = new C();
tC->c = 8;
printf("C: %d\n", sizeof(C));
printf("C data: %d\n", tC->getData());
A* aC = tC;
aC->print();
printf("D: %d\n", sizeof(D));
return 0;
}
我的预期输出是:
A:8
数据:1
B:12
B数据:4
C:16
C数据:8
世界
D:8
然而,我得到的输出是:
A:16
数据:1
B:16
B数据:4
C:24
C数据:8
世界
D:8
有人知道这里发生了什么吗?谢谢!
答案 0 :(得分:3)
类A到C的实例包含vptr,指向动态类型的虚函数表的指针。此指针在64位计算机上占用8个字节(或在32位计算机上占用4个字节)。每个int成员占用4个字节。
sizeof(Class)的最小值是所有成员的sizeof(成员)的总和。如果是这样,那么
sizeof(A) = 8 (vptr) + 4 (int a) = 12
sizeof(B) = 8 (vptr) + 4 (int a) + 4 (int b) = 16
sizeof(C) = 8 (vptr) + 4 (int a) + 4 (int b) + 4 (int c) = 20
sizeof(D) = 4 (int a) + 4 (int b) = 8
但是,这只是最小尺寸。编译器通常会将此大小增加到sizeof(void *)的倍数,这里是8个字节。此过程称为对齐。看起来这可能会浪费内存,但性能提升会超过这一点:CPU可以比非对齐数据更快地读取对齐数据。
顺便说一句,如果您使用的是32位计算机,那么您的预期结果将是正确的。指针(特别是vptr)在那里是4字节宽,并且对齐也是4字节的倍数。由于所讨论的类的所有数据成员都是4字节大,因此对齐不会在那里做任何事情。