我一直想知道这种事情,所以我决定想出一个完整的答案。这是关于你可能期望的,它是可以预测的(耶)!因此,通过以下信息,您应该能够预测类的大小。
使用 Visual Studio Community 2017(版本 15.2),在禁用所有优化和 RTTI(运行时类型信息)关闭的发布模式下,我确定了以下内容:
简短的回答:
首先:
- 在 32 (x86) 位中,
<size of pointer> == 4
字节
- 在 64 (x64) 位中,
<size of pointer> == 8
字节
- 当我说“虚拟类继承”时,我的意思是:
class ChildClass: virtual public ParentClass
现在,我的发现是:
- 空类为 1 个字节
- 空类的继承还是1字节
- 具有函数的空类仍然是 1 个字节(?!请参阅下面的注释以获取解释)
- 用函数继承空类还是1字节
- 将变量添加到空类是
<size of variable>
字节
- 继承一个带有变量的类并添加另一个变量是
<size of variables>
字节
- 继承一个类并覆盖它的函数会添加一个 vtable(在结论部分提供了进一步的解释)并且是
<size of pointer>
字节
- 简单地声明一个函数 virtual 也会添加一个 vtable,使其成为
<size of pointer>
字节
- 空类的虚拟类继承(有或没有成员函数)也添加了一个 vtable,并使类
<size of pointer>
字节
- 非空类的虚拟类继承也添加了一个 vtable,但它变得有些复杂:它将字节添加
<size of pointer>
到总数中,将所有成员变量包装<size of pointer>
成尽可能多的字节增量以覆盖<total size of member variables>
- 是的,你没看错...(请参阅我对结论中发生的事情的猜测...)
请注意,我什至尝试让 function() cout 一些文本,创建类的实例并调用该函数;它不会改变函数类的大小(这不是优化)!我有点惊讶,但这实际上是有道理的:成员函数不会改变,因此它们可以存储在类本身的外部。
结论:
- 空类是 1 个字节,因为这是它在内存中存在的最低要求。但是,一旦添加了数据或 vtable 数据,就从 0 字节开始计数。
- 添加(非虚拟)成员函数对大小没有任何影响,因为成员函数存储在外部。
- 将成员函数声明为虚拟(即使该类没有被覆盖!)或覆盖子类中的成员函数会添加所谓的“vtable”或“虚拟函数表”,这允许动态调度(这实际上是不过使用起来超级棒,我强烈推荐使用它)。此 vtable 消耗
<size of pointer>
字节,将<size of pointer>
字节添加到所述类。当然,这个 vtable 每个类只能存在一次(它存在或不存在)。
- 添加成员变量会通过该成员变量增加类的大小,无论所述成员变量是在父类还是子类中(当然,父类仍然保持自己的大小)。
- 虚拟类继承是唯一变得复杂的部分......所以......我认为经过一些实验后发生的事情是:类的大小实际上
<size of pointer>
一次以字节为单位递增,即使它不需要消耗这么多内存,我猜是因为它为每个<size of pointer>
字节的内存或其他东西添加了一个vtable“帮助块”......
长答案:
我使用以下代码确定了所有这些:
#include <iostream>
using namespace std;
class TestA
{
};
class TestB: public TestA
{
};
class TestC: virtual public TestA
{
};
class TestD
{
public:
int i;
};
class TestE: public TestD
{
public:
int j;
};
class TestF: virtual public TestD
{
public:
int j;
};
class TestG
{
public:
void function()
{
}
};
class TestH: public TestG
{
public:
void function()
{
}
};
class TestI: virtual public TestG
{
public:
void function()
{
}
};
class TestJ
{
public:
virtual void function()
{
}
};
class TestK: public TestJ
{
public:
void function() override
{
}
};
class TestL: virtual public TestJ
{
public:
void function() override
{
}
};
void main()
{
cout << "int:\t\t" << sizeof(int) << "\n";
cout << "TestA:\t\t" << sizeof(TestA) << "\t(empty class)\n";
cout << "TestB:\t\t" << sizeof(TestB) << "\t(inheriting empty class)\n";
cout << "TestC:\t\t" << sizeof(TestC) << "\t(virtual inheriting empty class)\n";
cout << "TestD:\t\t" << sizeof(TestD) << "\t(int class)\n";
cout << "TestE:\t\t" << sizeof(TestE) << "\t(inheriting int + int class)\n";
cout << "TestF:\t\t" << sizeof(TestF) << "\t(virtual inheriting int + int class)\n";
cout << "TestG:\t\t" << sizeof(TestG) << "\t(function class)\n";
cout << "TestH:\t\t" << sizeof(TestH) << "\t(inheriting function class)\n";
cout << "TestI:\t\t" << sizeof(TestI) << "\t(virtual inheriting function class)\n";
cout << "TestJ:\t\t" << sizeof(TestJ) << "\t(virtual function class)\n";
cout << "TestK:\t\t" << sizeof(TestK) << "\t(inheriting overriding function class)\n";
cout << "TestL:\t\t" << sizeof(TestL) << "\t(virtual inheriting overriding function class)\n";
cout << "\n";
system("pause");
}
输出:
32 (x86) 位:
int: 4
TestA: 1 (empty class)
TestB: 1 (inheriting empty class)
TestC: 4 (virtual inheriting empty class)
TestD: 4 (int class)
TestE: 8 (inheriting int + int class)
TestF: 12 (virtual inheriting int + int class)
TestG: 1 (function class)
TestH: 1 (inheriting function class)
TestI: 4 (virtual inheriting function class)
TestJ: 4 (virtual function class)
TestK: 4 (inheriting overriding function class)
TestL: 8 (virtual inheriting overriding function class)
64 (x64) 位:
int: 4
TestA: 1 (empty class)
TestB: 1 (inheriting empty class)
TestC: 8 (virtual inheriting empty class)
TestD: 4 (int class)
TestE: 8 (inheriting int + int class)
TestF: 24 (virtual inheriting int + int class)
TestG: 1 (function class)
TestH: 1 (inheriting function class)
TestI: 8 (virtual inheriting function class)
TestJ: 8 (virtual function class)
TestK: 8 (inheriting overriding function class)
TestL: 16 (virtual inheriting overriding function class)
如果您想了解有关多重继承的信息,请自己弄清楚!-.-