这是一个在线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?
向空类添加函数不会改变其大小?
感谢任何帮助。
感谢
答案 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?
A
和C
的大小为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
更多的存储空间。 A
和C
都不需要任何存储,但由于语言要求同一个类的不同实例具有不同的指针,因此它们的大小不能为零 - 因此编译器会使它们变小因为它可以,即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
一样。所以答案与G
和H
相同。
成员/函数的排序在这里无关紧要,因为只有一个成员变量,如果有的话,vtable ptr总是先行。
为什么siszeof(D)为8但sizeof(E)为16?
D
没有成员变量,但它有一个虚函数,所以它需要一个vtable指针。 vtable指针是它唯一需要的东西,所以它的大小为sizeof(void*)
,即8个字节。 E
需要与D
相同,加上整数的4个字节,编译器将其四舍五入以进行对齐。
答案 2 :(得分:1)
这是因为C ++标准禁止大小为0的类/结构。这就是为什么空结构/类的大小为1。 我发现它很烦人,但他们有一些理由。
这是int的大小,简单明了:)
这是一个空结构的大小(见A)。非虚函数根本不会影响对象大小。您无需存储在对象中以便能够调用其非虚拟成员函数。
我假设您正在构建一个64位应用程序。具有至少1个虚函数的任何类都具有指向虚方法表的指针。这允许您调用正确的虚函数,即使对象指针已转换为某个父类。指针通常被称为 vtable 。更多维基阅读:http://en.wikipedia.org/wiki/Virtual_method_table
我认为大小8来自那个64位指针。
要存储指针和int,技术上需要12个字节。但是,指针必须与8个字节对齐。现在设想自己创建一个对象数组A. A[0].vtable
将具有地址&amp; A + 0,A[0].i
将位于&A+8
,A[1].vtable
将位于{{1}我们有一个问题,12不能被8分割。这就是编译器创建填充的原因。它添加了额外的无用字节,以使对象在数组中正确对齐。在这种情况下,可分为8的最低值为16.因此大小。
与C的情况相同 - 非虚拟功能根本不对大小有贡献,因此您的大小与B匹配。
sizeof(G)== 4 - 与F
的sizeof(H)== 16
虚拟功能的数量无关紧要。对象中仍然只有一个vtable指针。如果放置更多虚函数,虚拟表会变大,但不会变大对象本身。在许多面向对象的程序中,您经常会遇到许多虚函数。将指针直接存储在对象本身中会很浪费。
这就是为什么H的大小(和解释)与E的大小匹配。
答案 3 :(得分:0)
为什么不尝试打印布局?来自system V abi
例如(将您的代码输入,并记住将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上查看。
以不同的顺序声明更改类的大小