sizeof类,带有int,函数,C ++中的虚函数?

问题描述 投票:16回答:5

这是一个在线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?向空类添加函数不会改变其大小?

感谢您的帮助。

感谢

c++ class object virtual sizeof
5个回答
40
投票

首先,虚拟函数不是具有8个字节的指针。在C ++中,只能保证sizeof(char)为任意数量的字节。

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

之所以会发生这种情况,是因为类实例不保存指向方法本身的指针,而是指向virtual function table的指针,该指针每个类一个。

因此,如果您有:

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) == 4Hint外,还具有指向vftable的指针(虚拟功能表,请参见上文)。此大小,int大小和分配与编译器有关。

为什么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 |

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没有虚函数,它只有一个整数,因此其布局类似于G的布局。因此答案与GH相同。

成员/函数的顺序在这里无关紧要,因为只有一个成员变量,而如果有一个成员变量,则vtable ptr始终排在第一位。

为什么siszeof(D)是8,而sizeof(E)是16?

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


1
投票
  • sizeof(A)== 1

之所以如此,是因为C ++标准禁止大小为0的类/结构。这就是为什么空的结构/类的大小为1的原因。我觉得这很烦人,但是他们对此有一些道理。

  • sizeof(B)== 4

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

  • sizeof(C)== 1

这是一个空结构的大小(请参阅A)。非虚函数完全不影响对象大小。您无需在对象中存储任何内容即可调用其非虚拟成员函数。

  • sizeof(D)== 8

我假设您正在构建64位应用程序。具有至少一个虚函数的任何类都具有指向虚方法表的指针。即使将对象指针强制转换为某个父类,这也允许您调用正确的虚函数。通常将指针称为v​​table。在Wiki上阅读更多内容:http://en.wikipedia.org/wiki/Virtual_method_table

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

  • sizeof(E)== 16

为了存储指针和int,从技术上讲,您需要12个字节。但是,指针必须对齐8个字节。现在设想自己创建一个对象E的数组A。A[0].vtable的地址为&A + 0,A[0].i的地址为&A+8A[1].vtable的地址为&A+12 –糟糕,我们有一个问题,12不能被8整除。这就是编译器创建padding的原因。它添加了其他无用的字节,以使对象在数组中正确对齐。在这种情况下,可以除以8的数的最小数为16。因此是大小。

  • sizeof(F)== 4

与C的情况相同-非虚函数完全不影响大小,因此您的大小匹配B。

  • sizeof(G)== 4-与F相同>]

  • sizeof(H)== 16

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

这就是H的大小(和说明)与E的大小匹配的原因。>>

为什么不尝试打印布局?来自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]
    
    

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

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

    而且您不能简单地添加所有字节,因此请在Google上进行填充检查的概念。

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


    0
    投票

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


    -1
    投票

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

    © www.soinside.com 2019 - 2024. All rights reserved.