sizeof类,在C++中有int,function,virtual函数

sizeof class with int , function, virtual function in C++?

本文关键字:function virtual int 函数 C++ sizeof 中有      更新时间:2023-10-16

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

感谢您的帮助。

感谢

首先,虚拟函数不是一个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)

现在:

为什么(A)的大小是1,(C)的大小也是1?

CCD_ 12和CCD_。函数与它无关。它只是一个伪字节。

为什么(H)的大小是16,而(G)的大小却是4?

G只有一个占内存的成员——int。在您的平台上,sizeof(int) == 4。除了int之外,H还具有指向vftable(虚拟函数表,见上文)的指针。this的大小、int的大小和allignment都是编译器特定的。

为什么(E)的大小是16,而(F)的大小却是4?

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

为什么(D)的大小是8,而(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 |

为什么(A)的大小是1,(C)的大小也是1?

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

为什么(H)的大小是16,而(G)的大小却是4?

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

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

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

为什么(E)的大小是16,而(F)的大小却是4?

E有虚拟函数,所以需要一个vtable ptr,所以它的布局就像H的一样。F没有虚函数,只有一个int,所以它的布局和G的一样。因此答案与CCD_ 35和CCD_。

成员/函数的顺序在这里并不重要,因为只有一个成员变量,如果有,vtable-ptr总是排在第一位。

为什么(D)的大小是8,而(E)的大小却是16?

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

  • sizeof(A)==1

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

  • 大小(B)==4

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

  • 大小(C)==1

这是一个空结构的大小(参见A)。非虚拟函数对对象大小没有任何影响。对象中不需要存储任何内容就可以调用其非虚拟成员函数。

  • 尺寸(D)==8

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

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

  • 大小(E)==16

要存储指针和int,从技术上讲需要12个字节。但是,指针必须与8个字节对齐。现在设想自己创建一个对象E的数组A。CCD_;A+0,A[0].i&A+8A[1].vtable&A+12——哇,我们有一个问题,12不可除以8。这就是编译器创建填充的原因。它添加了额外的无用字节,以使对象在数组中正确对齐。在这种情况下,被8除数的最低值是16。因此尺寸。

  • (F)的大小==4

与C的情况相同——非虚拟函数对大小没有任何贡献,所以你的大小与B.匹配

  • sizeof(G)==4——与F 相同

  • 尺寸(H)==16

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

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

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

你不能简单地添加所有的字节,有填充的概念,请在谷歌上查看。

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