2012-02-13 29 views
1

我有关于对象的大小以下代码:类的大小 - C++

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

到目前为止你有什么? – sth 2012-02-13 11:11:24

+0

谁说是12? – rve 2012-02-13 11:13:39

+1

相关:http://stackoverflow.com/q/7453269/922184 – Mysticial 2012-02-13 11:18:15

回答

8

sizeof(int A::_i) + sizeof(int C::_i) + sizeof(pointer to virtual table)

所有这些部分的大小依赖于实现的,在你的情况下,每一个有大小4

+0

为什么我有sizeof虚拟表?什么虚拟功能? – 2012-02-13 11:15:01

+1

@gfjhgrjrerjhm:虚函数是带有'virtual'关键字的函数。查看您的代码并搜索该关键字以回答“什么是虚拟功能?” – PlasmaHH 2012-02-13 11:18:27

+1

@MikeSeymour所以你说什么 - 我有两个虚函数,所以最终的计算将是16 ... 2虚拟和2整数 – 2012-02-13 11:18:43

5

的几乎任何一类的大小是依赖于实现,但我会 猜你是一个32位的机器上,而C类包含一个4字节 vptr和两个四个字节0​​(A::_iC::_i) 。

+0

你的意思是什么vptr? – 2012-02-13 11:16:34

+0

指向'vtbl'的指针。它是编译器插入对象的附加信息,以帮助动态解析虚函数调用。 – 2012-02-13 11:41:40

1

对象的大小将是特定的平台。例如在一个64位的平台上,我期望的大小是24个字节。一个物体的大小是多少有点棘手。它是由多种成分组成:

  • 大小的明显组成部分是数据成员:在你的情况,你有两个int层次结构中的某处,即这将有助于2 * sizeof(int)
  • 众所周知的隐藏组件是指向“vtable”的指针,即指向某种数据结构的指针,它处理调用虚拟函数的方式。这通常会贡献一个指针大小。
  • 可能完全被大多数人忽略的是隐藏指针与多重继承来,尤其是当对象涉及一个虚拟基类时:为了使对象看起来好像它是一个特定的层次结构,它将包含多于“vtable”指针。
  • 虽然许多人都知道更多的是“填充”,即隐藏字节用于确保数据成员与CPU所偏好的地址对齐。一般来说,如果存在相应的基本类型的大小,那么首选的对齐方式大致是类型的大小,直到给定系统上的高速缓存行的大小。也就是说,通常最大对齐是16个字节。
  • 最后,我可以想到一个有趣的空类:因为子对象需要具有不同的地址,所以每个空类将被视为至少有一个字节大,除非空类是基类,在这种情况下它可能与其他基地共享相同的地址。

大多数隐藏的东西并不适用,但对你的类必须2 * sizeof(int) + sizeof(T*)用于访问虚函数表的一个合适的类型。

0

C c;的大小实际上是实现的定义。如果你有任何代码实际上依赖于这个大小,这个代码是非常错误的,并且在编译器切换时可能会中断。

我们实际工作的问题:你很可能要么认为C必须包含在Cint的大小或者您会想到它有intC大小加上从A之一。

这两种猜测都是错误的,原因有二:

  • 有可能是所谓的填充你的结构。有时类型需要与特定边界对齐。为了执行这些对齐,编译器会在字段之间引入一些“浪费”空间的区域,以将它们保留在这些位置。你永远不能依赖这个填充的数量。

  • 还有一点,我主要是认为你的老师想告诉你:如果你写c.getI(),计算机必须知道要调用哪个方法,即是否调用从ABC之一。这些信息需要存储在某个地方。这个信息的存储为你的结构增加了一些额外的大小,但你永远不能依赖于将被添加多少。有些人可能会试图告诉你,这只能通过使用一个指针来存储,但这是不正确。编译器可以使用尽可能多的空间来存储这些信息。大多数编译器只出于效率原因而使用一个指针,但如果你依赖这个指针,那么你的代码对于编译器来说是错误的,因为编译器的处理方式不同。

如果你想知道更多,只需谷歌的“虚拟方法调用”,你可以找到这个用例的常见实现的一些例子。

0

我想你使用的是32位编译器。 这是C类的内存布局:

class C size(12): 
    +--- 
    | +--- (base class B) 
    | | +--- (base class A) 
0 | | | {vfptr} 
4 | | | _i 
    | | +--- 
    | +--- 
8 | _i 
    +--- 

C::[email protected]: 
    | &C_meta 
    | 0 
0 | &C::getI 
1 | &C::setI 

此基础上,你可以看到哪些部分进行阶级的大小。

您可以阅读更多关于虚拟功能,虚拟继承,虚拟表格的信息,找出它们的组织方式并为课程大小做出贡献。

如果您使用MVSC,则在编译时可以使用-d1reportAllClassLayout选项查看您的类的布局。