我有以下关于对象大小的代码:
class A
{
public:
int _i;
virtual int getI () = 0;
int setI (int i);
};
class B : public A
{
public:
int getI ();
virtual int setI (int i);
};
class C : public B
{
public:
int _i;
int getI ();
int setI (int i);
};
int main ()
{
B b;
C c;
}
为什么大小 C c; 是12?尺寸计算中包含哪些部分?
答案 0 :(得分:8)
sizeof(int A::_i) + sizeof(int C::_i) + sizeof(pointer to virtual table)
所有这些部分的大小都与实现有关,在您的情况下,每个部分的大小为4。
答案 1 :(得分:5)
任何类的大小都取决于实现,但我会
猜测你是32位机器,C类包含4字节
vptr
和两个四字节int
(A::_i
和C::_i
)。
答案 2 :(得分:1)
对象的大小将是特定于平台的。例如,在64位平台上,我希望大小为24字节。什么构成对象的大小有点棘手。它由各种组件组成:
int
,即这将贡献2 * sizeof(int)
。大多数隐藏的东西都不适用,但是对于你的班级,你有2 * sizeof(int) + sizeof(T*)
用于访问虚拟功能表的合适类型。
答案 3 :(得分:0)
C c;
的大小实际上是实现定义的。如果您有任何实际依赖于大小的代码,则此代码非常错误,并且无论何时切换编译器都可能会中断。
现在回答您的所有问题:您可能认为C
的大小与int
中包含的C
相同,或者您认为它的大小为int
来自C
的{1}}加上A
的{1}}。
这两种猜测都是错误的,原因有两个:
您的结构中可能存在所谓的填充。有时类型需要与某些边界对齐。为了强制执行这些对齐,编译器会在字段之间引入一些“浪费”空间区域,以使它们保留在这些位置。你永远不能依赖这个填充量。
还有另一点,我主要认为你的老师想要告诉你:如果你写c.getI()
,计算机必须知道要调用哪种方法,即是否从{{{ 1}},A
或B
。这些信息需要存储在某个地方。此信息的存储为您的结构添加了一些额外的大小,但您永远不能依赖于将添加多少。有些人可能会试图告诉您这只是使用一个指针存储,但这是不正确。允许编译器使用尽可能多的空间来存储这些信息。出于效率原因,大多数编译器只使用一个指针,但是如果依赖于此,那么对于执行此操作的编译器来说,代码将是错误的。
如果您想了解更多信息,只需谷歌“虚拟方法调用”,您就可以找到一些针对此用例的常见实现示例。
答案 4 :(得分:0)
我认为你使用的是32位编译器。 这是C类的内存布局:
class C size(12):
+---
| +--- (base class B)
| | +--- (base class A)
0 | | | {vfptr}
4 | | | _i
| | +---
| +---
8 | _i
+---
C::$vftable@:
| &C_meta
| 0
0 | &C::getI
1 | &C::setI
在此基础上,您可以看到哪些部分符合班级规模。
您可以阅读有关虚拟功能,虚拟继承,虚拟表的更多信息,以了解它们的组织方式并为课堂规模做出贡献。
如果使用MVSC,则在编译时可以使用-d1reportAllClassLayout选项来查看类的布局。