在C ++中使用int,function,virtual函数的sizeof类?

时间:2012-02-24 22:59:05

标签: c++ class object virtual sizeof

这是一个在线C ++测试问题,已经完成。

#include<iostream>
using namespace std; 
class A
{

};
class B
{
int i; 
}; 

class C
{
void foo();
};
class D
{
virtual void foo();
};

class E
{
int i ; 
    virtual void foo();
};
class F
{
int i; 
    void foo();
};
class G
{
    void foo();
    int i;
    void foo1();
};

class H
{
    int i ;
    virtual void foo();
    virtual void foo1();
};
int main()
{
cout <<"sizeof(class A) : " << sizeof(A) << endl ;
cout <<"sizeof(class B) adding the member int i : " << sizeof(B) << endl ;
cout <<"sizeof(class C) adding the member void foo() : " << sizeof(C) << endl ;
cout <<"sizeof(class D) after making foo virtual : " << sizeof(D) << endl ;
cout <<"sizeof(class E) after adding foo virtual , int : " << sizeof(E) << endl ;
cout <<"sizeof(class F) after adding foo  , int : " << sizeof(F) << endl ;
cout <<"sizeof(class G) after adding foo  , int : " << sizeof(G) << endl ;
G g;
cout <<"sizeof(class G) after adding foo  , int : " << sizeof(g) << endl ;
cout <<"sizeof(class H) after adding int 2 virtual " << sizeof(H) << endl ;
return 0; 
}

输出:

sizeof(class A) : 1
sizeof(class B) adding the member int i : 4
sizeof(class C) adding the member void foo() : 1
sizeof(class D) after making foo virtual : 8
sizeof(class E) after adding foo virtual , int : 16
sizeof(class F) after adding foo  , int : 4
sizeof(class G) after adding foo   , unsigned int : 4
sizeof(class g) after adding foo  , unsigned int : 4
sizeof(class H) after adding int 2 virtual 16

我的问题:

为什么siszeof(A)为1而sizeof(C)为1?

为什么siszeof(H)为16但sizeof(G)为4?

为什么siszeof(E)为16但sizeof(F)为4?

为什么siszeof(D)为8但sizeof(E)为16?

我的猜测:

虚函数是一个8字节的指针。 但是,我不知道为什么E大小为16? 向空类添加函数不会改变其大小?

感谢任何帮助。

感谢

5 个答案:

答案 0 :(得分:37)

首先,虚函数不是8字节的指针。在C ++中,除了sizeof(char)之外什么都不保证是任意数量的字节。

其次,只有类中的第一个虚函数增加了它的大小(依赖于编译器,但在大多数情况下 - 如果不是全部的话 - 就像这样)。所有后续方法都没有。非虚函数不会影响类的大小。

这是因为类实例不保存指向方法本身的指针,而是指向虚函数表,每个类一个。

所以如果你有:

class A
{
   virtual void foo();
}

class B
{
   virtual void goo();
   virtual void test();
   static void m();
   void x();
}

你会sizeof(A) == sizeof(B)

现在:

  

为什么siszeof(A)是1而sizeof(C)也是1?

AC的大小为1,因为它不允许类的大小为0.这些函数与它无关。它只是一个虚拟字节。

  

为什么siszeof(H)为16但sizeof(G)为4?

G只有一个会员负责记忆 - int。在您的平台上,sizeof(int) == 4。除了H之外,int还有一个指向vftable的指针(虚函数表,见上文)。这个大小,int和allignment的大小是编译器特定的。

  

为什么siszeof(E)为16但sizeof(F)为4?

上面解释 - 非虚拟方法不占用类中的内存。

  

为什么siszeof(D)为8但sizeof(E)为16?

D仅包含vftable指针,在您的平台上显然是8个字节。 E也有一个int,vftable与8个字节对齐。所以它就像:

class E

4 bytes for int |  4 padding bytes  |  8 bytes for vftable pointer  | 
| x | x | x | x |    |    |    |    | v | v | v | v | v | v | v | v |

答案 1 :(得分:3)

  

为什么siszeof(A)是1而sizeof(C)也是1?

C中的函数不是虚拟的,因此该类不需要vtable指针,因此它不需要比A更多的存储空间。 AC都不需要任何存储,但由于语言要求同一个类的不同实例具有不同的指针,因此它们的大小不能为零 - 因此编译器会使它们变小因为它可以,即1个字节。

  

为什么siszeof(H)为16但sizeof(G)为4?

G没有虚函数,因此它需要存储的只是int,在编译器和体系结构上是4字节。

H具有虚函数,因此该类需要包含int和vtable指针。所有广泛使用的编译器都将vtable指针存储在类的开头,因此布局为{vptr,int},如果您使用的是64位主机,则为8 + 4 = 12个字节。

但是,编译器可以自由地将其填充到16个字节,这样如果在一个数组中分配了H的多个实例,那么所有这些实例都将是字对齐的。这很重要,因为如果它不是字对齐的,那么访问指针(即这里的vtable指针)会产生重大的性能影响。

  

为什么siszeof(E)为16但sizeof(F)为4?

E有虚函数,所以需要一个vtable ptr,所以它的布局就像H一样。 F没有虚函数,它只有一个int,所以它的布局就像G一样。所以答案与GH相同。

成员/函数的排序在这里无关紧要,因为只有一个成员变量,如果有的话,vtable ptr总是先行。

  

为什么siszeof(D)为8但sizeof(E)为16?

D没有成员变量,但它有一个虚函数,所以它需要一个vtable指针。 vtable指针是它唯一需要的东西,所以它的大小为sizeof(void*),即8个字节。 E需要与D相同,加上整数的4个字节,编译器将其四舍五入以进行对齐。

答案 2 :(得分:1)

  • 的sizeof(A)== 1

这是因为C ++标准禁止大小为0的类/结构。这就是为什么空结构/类的大小为1。 我发现它很烦人,但他们有一些理由。

  • 的sizeof(B)== 4

这是int的大小,简单明了:)

  • 的sizeof(C)== 1

这是一个空结构的大小(见A)。非虚函数根本不会影响对象大小。您无需存储在对象中以便能够调用其非虚拟成员函数。

  • 的sizeof(d)== 8

我假设您正在构建一个64位应用程序。具有至少1个虚函数的任何类都具有指向虚方法表的指针。这允许您调用正确的虚函数,即使对象指针已转换为某个父类。指针通常被称为 vtable 。更多维基阅读:http://en.wikipedia.org/wiki/Virtual_method_table

我认为大小8来自那个64位指针。

  • 的sizeof(E)== 16

要存储指针和int,技术上需要12个字节。但是,指针必须与8个字节对齐。现在设想自己创建一个对象数组A. A[0].vtable将具有地址&amp; A + 0,A[0].i将位于&A+8A[1].vtable将位于{{1}我们有一个问题,12不能被8分割。这就是编译器创建填充的原因。它添加了额外的无用字节,以使对象在数组中正确对齐。在这种情况下,可分为8的最低值为16.因此大小。

  • 的sizeof(F)== 4

与C的情况相同 - 非虚拟功能根本不对大小有贡献,因此您的大小与B匹配。

  • sizeof(G)== 4 - 与F

  • 相同
  • 的sizeof(H)== 16

虚拟功能的数量无关紧要。对象中仍然只有一个vtable指针。如果放置更多虚函数,虚拟表会变大,但不会变大对象本身。在许多面向对象的程序中,您经常会遇到许多虚函数。将指针直接存储在对象本身中会很浪费。

这就是为什么H的大小(和解释)与E的大小匹配。

答案 3 :(得分:0)

为什么不尝试打印布局?来自system V abi

  • dsize(O):对象的数据大小,即不带尾部填充的O的大小。
  • nvsize(O):对象的非虚拟大小,即不带虚拟基数的O的大小。
  • nvalign(O):对象的非虚拟对齐方式,即没有虚拟碱基的O的对齐方式。

例如(将您的代码输入,并记住将sizeof应用于您要检查的类型)

struct Base1 {
  virtual int method_base_11() {
    return 11;
  }
  virtual ~Base1() = default;
};

struct Base2 {
  virtual int method_base_21() {
    return 22;
  }
  virtual ~Base2() = default;
};

struct Foo: public Base1, public Base2 {
  int a;
};

int main() {
  Foo foo;
  foo.method_base_21();
  return sizeof(Foo);
}

输出

$ clang -cc1 -std=c++11 -fdump-record-layouts foo.cc

*** Dumping AST Record Layout
         0 | struct Base1
         0 |   (Base1 vtable pointer)
           | [sizeof=8, dsize=8, align=8,
           |  nvsize=8, nvalign=8]

*** Dumping AST Record Layout
         0 | struct Base2
         0 |   (Base2 vtable pointer)
           | [sizeof=8, dsize=8, align=8,
           |  nvsize=8, nvalign=8]

*** Dumping AST Record Layout
         0 | struct Foo
         0 |   struct Base1 (primary base)
         0 |     (Base1 vtable pointer)
         8 |   struct Base2 (base)
         8 |     (Base2 vtable pointer)
        16 |   int a
           | [sizeof=24, dsize=20, align=8,
           |  nvsize=20, nvalign=8]

答案 4 :(得分:-1)

带填充的int的大小和虚函数= 18字节

简单函数字节= 1 虚函数= 8

你不能简单地添加所有字节,其中有填充概念在google上查看。

以不同的顺序声明更改类的大小