在 C++ 中如何确定对象的大小?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/937773/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How do you determine the size of an object in C++?
提问by Joel
For example, say I have a class Temp:
例如,假设我有一个 Temp 类:
class Temp
{
public:
int function1(int foo) { return 1; }
void function2(int bar) { foobar = bar; }
private:
int foobar;
};
When I create an object of class Temp, how would I calculate how much space it needs, and how is it represented in memory (e.g.| 4 bytes for foobar| 8 bytes for function1 | etc | )
当我创建一个 Temp 类的对象时,我将如何计算它需要多少空间,以及它在内存中的表示方式(例如 | foobar 的 4 个字节| function1 的 8 个字节 | etc | )
回答by Drew Hall
To a first order approximation, the size of an object is the sum of the sizes of its constituent data members. You can be sure it will never be smaller than this.
对于一阶近似,对象的大小是其组成数据成员的大小之和。您可以确定它永远不会比这更小。
More precisely, the compiler is entitled to insert padding space between data members to ensure that each data member meets the alignment requirements of the platform. Some platforms are very strict about alignment, while others (x86) are more forgiving, but will perform significantly better with proper alignment. So, even the compiler optimization setting can affect the object size.
更准确地说,编译器有权在数据成员之间插入填充空间,以确保每个数据成员都满足平台的对齐要求。一些平台对对齐非常严格,而其他平台 (x86) 更宽容,但在正确对齐的情况下会表现得更好。因此,即使是编译器优化设置也会影响对象大小。
Inheritance and virtual functions add an additional complication. As others have said, the member functions of your class themselves do not take up "per object" space, but the existence of virtual functions in that class's interface generally implies the existence of a virtual table, essentially a lookup table of function pointers used to dynamically resolve the proper function implementation to call at runtime. The virtual table (vtbl) is accessed generally via a pointer stored in each object.
继承和虚函数增加了额外的复杂性。正如其他人所说,您的类的成员函数本身并不占用“每个对象”的空间,但是该类接口中虚函数的存在通常意味着存在一个虚表,本质上是一个用于查找函数指针的表动态解析正确的函数实现以在运行时调用。虚拟表 (vtbl) 通常通过存储在每个对象中的指针来访问。
Derived class objects also include all data members of their base classes.
派生类对象还包括其基类的所有数据成员。
Finally, access specifiers (public, private, protected) grant the compiler certain leeway with packing of data members.
最后,访问说明符(公共的、私有的、受保护的)在数据成员的打包方面为编译器提供了一定的余地。
The short answer is that sizeof(myObj) or sizeof(MyClass) will always tell you the proper size of an object, but its result is not always easy to predict.
简短的回答是 sizeof(myObj) 或 sizeof(MyClass) 将始终告诉您对象的正确大小,但其结果并不总是容易预测。
回答by Todd Gardner
sizeof(Temp)
will give you the size. Most likely, it is 4 bytes (given a whole lot of assumptions) and that is only for the int. The functions do not take up any room on a per object basis, they are compiled once, and linked by the compiler each time they are used.
会给你尺寸。最有可能的是 4 个字节(考虑到很多假设)并且仅适用于 int。这些函数在每个对象的基础上不占用任何空间,它们被编译一次,并在每次使用时由编译器链接。
It's impossible to say exactly what the object layout is, however, the standard doesn't define the binary representation for objects.
不可能确切地说对象布局是什么,但是,标准没有定义对象的二进制表示。
There are a few things to be aware of with binary representations, like they aren't necessarily the sum of the bytes of the data members, due to things like structure padding
使用二进制表示需要注意一些事项,例如由于结构填充等原因,它们不一定是数据成员的字节总和
回答by Andrew
I've always wondered this sort of thing, so I decided to come up with a complete answer. It's about what you might expect, and it is predictable (yay)! Thus, with the information below, you ought to be able to predict the size of a class.
我一直想知道这种事情,所以我决定想出一个完整的答案。这是关于您可能期望的,并且是可以预测的(是的)!因此,通过以下信息,您应该能够预测类的大小。
Using Visual Studio Community 2017 (Version 15.2), in Release mode with all optimizations disabled and RTTI (Run-time Type Information) off, I have determined the following:
使用 Visual Studio Community 2017(版本 15.2),在禁用所有优化和 RTTI(运行时类型信息)的发布模式下,我确定了以下内容:
Shortish answer:
简短的回答:
First of all:
首先:
- In 32 (x86) bit,
<size of pointer> == 4
bytes - In 64 (x64) bit,
<size of pointer> == 8
bytes - When I say "virtual class inheritance", I mean e.g.:
class ChildClass: virtual public ParentClass
- 在 32 (x86) 位,
<size of pointer> == 4
字节 - 在 64 (x64) 位,
<size of pointer> == 8
字节 - 当我说“虚拟类继承”时,我的意思是例如:
class ChildClass: virtual public ParentClass
Now, my findings are that:
现在,我的发现是:
- empty classes are 1 byte
- inheritance of an empty class is still 1 byte
- empty classes with functions are still 1 byte (?! see Notebelow for explanation)
- inheritance of an empty class with a function is still 1 byte
- adding a variable to an empty class is
<size of variable>
bytes - inheriting a class with a variable and adding another variable is
<size of variables>
bytes - inheriting a class and overriding its function adds a vtable (further explanation provided in Conclusionssection) and is
<size of pointer>
bytes - simply declaring a function virtual also adds a vtable, making it
<size of pointer>
bytes - virtual class inheritance of an empty class (with or without a member function) also adds a vtable, and makes the class
<size of pointer>
bytes - virtual class inheritance of a non-empty class also adds a vtable, but it gets somewhat complicated: it adds
<size of pointer>
bytes to the total, wrappingall of the member variables in as many<size of pointer>
bytes increments as is necessary to cover<total size of member variables>
- yeah, you read that right... (see my guess as to what's going on in Conclusions...)
- 空类是 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>
Notethat I even tried having the function() cout some text, creating an instance of the class, and calling the function; it doesn't change the size of the function class (it's not an optimization)! I was somewhat surprised, but it actually makes sense: member functions don't change, so they can be stored external to the class itself.
请注意,我什至尝试让 function() 输出一些文本,创建类的实例,然后调用该函数;它不会改变函数类的大小(这不是优化)!我有点惊讶,但它实际上是有道理的:成员函数不会改变,所以它们可以存储在类本身的外部。
Conclusions:
结论:
- Empty classes are 1 byte, since that is the minimum required for it to have a presence in memory. Once data or vtable data is added, though, begin counting at 0 bytes.
- Adding a (non-virtual) member function does nothing to the size, because the member function is stored externally.
- Declaring a member function to be virtual (even if the class is not overridded!) or overriding a member function in a child class adds what is called a "vtable" or "virtual function table", which allows for Dynamic Dispatch(which is really super awesome to use though and I highly recommend using it). This vtable consumes
<size of pointer>
bytes, adding<size of pointer>
bytes to said class. This vtable can only exist once per class (either it does or it doesn't), of course. - Adding a member variable increases the size of the class by that member variable, regardless of whether said member variable is in the parent or child class (the parent class remains its own size though, of course).
- Virtual class inheritance is the only part that gets complicated... So... I think what's going on after a little experimentation is: the size of the class actually increments in
<size of pointer>
bytes at a time, even if it doesn't need to consume that much memory, I'm guessing because it's adding a vtable "helper block" for each<size of pointer>
bytes of memory or something...
- 空类是 1 个字节,因为这是它在内存中存在的最低要求。但是,一旦添加了数据或 vtable 数据,就从 0 字节开始计数。
- 添加(非虚拟)成员函数对大小没有任何影响,因为成员函数存储在外部。
- 将成员函数声明为虚函数(即使类没有被覆盖!)或覆盖子类中的成员函数会添加所谓的“vtable”或“虚函数表”,这允许动态调度(这实际上是不过使用起来超级棒,我强烈建议使用它)。这个 vtable 消耗
<size of pointer>
字节,将<size of pointer>
字节添加到所述类。当然,这个 vtable 每个类只能存在一次(要么存在,要么不存在)。 - 添加成员变量会通过该成员变量增加类的大小,无论所述成员变量是在父类还是子类中(当然,父类仍保持其自己的大小)。
- 虚拟类继承是唯一变得复杂的部分......所以......我认为经过一些实验后发生的事情是:类的大小实际上以
<size of pointer>
字节为单位一次增加,即使它不需要消耗那么多内存,我猜是因为它为每个<size of pointer>
内存字节或其他东西添加了一个 vtable“帮助块” ......
Long answer:
长答案:
I determined all of this using this code:
我使用以下代码确定了所有这些:
#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");
}
Output:
输出:
32 (x86) bits:
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) bits:
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)
If you want information on multiple inheritance, go figure it out your darn self! -.-
如果您想要有关多重继承的信息,请自己弄清楚!-.-
回答by Paul Morie
If you want detailed information about how objects are represented in memory at run-time, the ABI (Application Binary Interface) specification is the place to look. You'll need to look determine which ABI your compiler implements; for example, GCC versions 3.2 and above implement the Itanium C++ ABI.
如果您想了解有关对象在运行时如何在内存中表示的详细信息,可以查看 ABI(应用程序二进制接口)规范。您需要查看确定您的编译器实现的 ABI;例如,GCC 3.2 及以上版本实现了Itanium C++ ABI。
回答by ephemient
Methods belong to the class, not any particular instantiated object.
方法属于类,而不是任何特定的实例化对象。
Unless there are virtual methods, the size of an object is the sum of the size of its non-static members, plus optional padding between the members for alignment. The members will probably be laid out sequentially in memory, but the spec doesn't guarantee ordering between sections with different access specifications, nor ordering relative to the layout of superclasses.
除非有虚方法,否则对象的大小是其非静态成员的大小之和,加上成员之间用于对齐的可选填充。成员可能会在内存中按顺序排列,但规范不保证具有不同访问规范的部分之间的排序,也不保证相对于超类布局的排序。
With virtual methods present, there may be additional space taken for vtableand other RTTI information.
使用虚拟方法时,可能会为vtable和其他 RTTI 信息占用额外的空间。
On most platforms, executable code goes in the read-only .text
(or similarly named) section of the executable or library, and is never copied anywhere. When class Temp
has a method public: int function1(int)
, the Temp
metadata may have a pointer to a _ZN4Temp9function1Ei
(mangled name may be different depending on compiler) function for the actual implementation, but certainly it would never contain the executable code embedded.
在大多数平台上,可执行代码位于.text
可执行文件或库的只读(或类似名称)部分,并且永远不会复制到任何地方。当class Temp
有一个方法时public: int function1(int)
,Temp
元数据可能有一个指向_ZN4Temp9function1Ei
实际实现的(根据编译器的不同名称可能会有所不同)函数的指针,但肯定它永远不会包含嵌入的可执行代码。
回答by Canopus
Member functions dont account for the size of the objects of a particular class. The size of the object depends only on the member variables. In case of classes that contain virtual functions, the VPTR gets added to the object layout. So the size of the objects is basically size of the member variables + the size of the VPTRs. Sometimes this may not be true as Compilers try to locate member variables at the DWORD boundary.
成员函数不考虑特定类的对象的大小。对象的大小仅取决于成员变量。对于包含虚函数的类,VPTR 被添加到对象布局中。所以对象的大小基本上是成员变量的大小+ VPTR 的大小。有时这可能不是真的,因为编译器试图在 DWORD 边界定位成员变量。
回答by Johannes Gerer
If you are using Microsoft Visual C++ there is one compiler option that tells you, how large your object actually is: /d1reportSingleClassLayout
如果您使用的是 Microsoft Visual C++,则有一个编译器选项会告诉您对象的实际大小:/d1reportSingleClassLayout
It's undocumented except for this video by Lavavej http://channel9.msdn.com/Shows/Going+Deep/C9-Lectures-Stephan-T-Lavavej-Advanced-STL-3-of-n
除了 Lavavej http://channel9.msdn.com/Shows/Going+Deep/C9-Lectures-Stephan-T-Lavavej-Advanced-STL-3-of-n
回答by Phil Miller
If you want to examine the layout of a particular structure, the offsetof(s,member)
macro may also be of use. It tells you how far from the base address of a structure a particular member lives:
如果要检查特定结构的布局,offsetof(s,member)
也可以使用宏。它告诉你一个特定成员离结构的基地址有多远:
struct foo {
char *a;
int b;
};
// Print placement of foo's members
void printFoo() {
printf("foo->a is %zu bytes into a foo\n", offsetof(struct foo, a));
printf("foo->b is %zu bytes into a foo\n", offsetof(struct foo, b));
}
int main() {
printFoo();
return 0;
}
Would print on a typical 32-bit machine:
将在典型的 32 位机器上打印:
foo->a is 0 bytes into a foo
foo->b is 4 bytes into a foo
Whereas on a typical 64 bit machine, it would print
而在典型的 64 位机器上,它会打印
foo->a is 0 bytes into a foo
foo->b is 8 bytes into a foo
回答by sybreon
Thismay help.
这可能会有所帮助。
Additionally, class functions are represented just like any other function. The only magic that C++ does to the function is to mangle the function names to uniquely identify a specific function with a specific set of parameters inside a specific class.
此外,类函数的表示方式与任何其他函数一样。C++ 对函数所做的唯一魔法是修改函数名称以唯一标识具有特定类中的特定参数集的特定函数。
回答by Phil Miller
There's a utility call pahole
(for 'Poke-A-HOLE') that's nominally intended to study how object layouts get padded, but is great for visualizing object size and layout in general.
有一个实用程序调用pahole
(用于'Poke-A-HOLE')名义上旨在研究对象布局如何被填充,但非常适合可视化对象大小和一般布局。