在 C++ 中如何确定对象的大小?
例如,假设我有一个 Temp 类:
class Temp
{
public:
int function1(int foo) { return 1; }
void function2(int bar) { foobar = bar; }
private:
int foobar;
};
当我创建 Temp 类的对象时,我将如何计算它需要多少空间,以及它在内存中如何表示(例如| foobar 为 4 个字节| function1 为 8 个字节 | 等等| )
For example, say I have a class 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 | )
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(11)
对于一阶近似,对象的大小是其组成数据成员的大小之和。 您可以确定它永远不会比这个小。
更准确地说,编译器有权在数据成员之间插入填充空间,以确保每个数据成员满足平台的对齐要求。 一些平台对对齐非常严格,而其他平台(x86)则更宽松,但通过正确的对齐会表现得更好。 因此,即使编译器优化设置也会影响对象大小。
继承和虚函数增加了额外的复杂性。 正如其他人所说,类本身的成员函数不占用“每个对象”空间,但该类接口中虚拟函数的存在通常意味着虚拟表的存在,本质上是一个用于函数指针的查找表动态解析正确的函数实现以在运行时调用。 虚拟表(vtbl)通常通过存储在每个对象中的指针来访问。
派生类对象还包括其基类的所有数据成员。
最后,访问说明符(公共、私有、受保护)为编译器提供了打包数据成员的一定余地。
简短的答案是 sizeof(myObj) 或 sizeof(MyClass) 总是会告诉您对象的正确大小,但其结果并不总是容易预测。
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.
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.
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.
我一直想知道这个问题,所以我决定给出一个完整的答案。 这与您可能期望的内容有关,并且是可预测的(是的)! 因此,根据以下信息,您应该能够预测班级的规模。
使用 Visual Studio Community 2017(版本 15.2),处于禁用所有优化和 RTTI 的发布模式 (运行时类型信息)关闭,我确定了以下内容:
简短答案:
首先:
== 4
字节<指针大小> == 8
bytesclass ChildClass: virtual public ParentClass
现在,我的发现是:
<变量大小>
字节<指针大小>
字节,<指针大小>
bytes
bytes<指针大小>
字节到总数,包装所有成员变量以尽可能多的<指针大小>
字节增量来覆盖<成员变量的总大小>
- 是的,你没看错。 (请参阅我对结论中发生的情况的猜测...)注意我什至尝试使用 function() 计算一些文本,创建该类的实例,并调用该函数; 它不会改变函数类的大小(这不是优化)! 我有点惊讶,但这实际上是有道理的:成员函数不会改变,因此它们可以存储在类本身的外部。
结论:
<指针大小>
字节,向所述类添加<指针大小>
字节。 当然,这个虚函数表在每个类中只能存在一次(要么存在,要么不存在)。
字节递增一次,即使它不需要消耗那么多内存,我猜测是因为它为每个<指针大小>
字节内存或其他东西添加了一个vtable“辅助块” ...长答案:
我使用以下代码确定了所有这些:
输出:
32 (x86) 位:
64 (x64) 位:
如果您想要有关多重继承的信息,去弄清楚你该死的自己! -.-
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:
Shortish answer:
First of all:
<size of pointer> == 4
bytes<size of pointer> == 8
bytesclass ChildClass: virtual public ParentClass
Now, my findings are that:
<size of variable>
bytes<size of variables>
bytes<size of pointer>
bytes<size of pointer>
bytes<size of pointer>
bytes<size of pointer>
bytes to the total, wrapping all 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...)Note that 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.
Conclusions:
<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.<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...Long answer:
I determined all of this using this code:
Output:
32 (x86) bits:
64 (x64) bits:
If you want information on multiple inheritance, go figure it out your darn self! -.-
方法属于类,而不属于任何特定的实例化对象。
除非有虚方法,否则对象的大小是其非静态成员的大小之和,加上成员之间用于对齐的可选填充。 成员可能会在内存中按顺序排列,但规范不保证具有不同访问规范的部分之间的排序,也不保证相对于超类布局的排序。
如果存在虚拟方法,vtable 和其他 RTTI 信息可能会占用额外的空间。
在大多数平台上,可执行代码位于可执行文件或库的只读
.text
(或类似名称)部分,并且永远不会复制到任何地方。 当class Temp
具有方法public: int function1(int)
时,Temp
元数据可能具有指向_ZN4Temp9function1Ei
的指针code>(损坏的名称可能会因编译器而异)函数用于实际实现,但肯定它永远不会包含嵌入的可执行代码。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 vtable and other RTTI information.
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. Whenclass Temp
has a methodpublic: int function1(int)
, theTemp
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.会给你尺寸。 最有可能的是,它是 4 个字节(考虑到很多假设),并且这只适用于 int。 这些函数在每个对象的基础上不占用任何空间,它们被编译一次,并在每次使用时由编译器链接。
不可能确切地说出对象布局是什么,但是,该标准没有定义对象的二进制表示形式。
对于二进制表示,需要注意一些事情,比如它们不一定是数据成员的字节总和,因为 结构填充
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.
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
成员函数不考虑特定类的对象的大小。 对象的大小仅取决于成员变量。 如果类包含虚函数,VPTR 将添加到对象布局中。 所以对象的大小基本上是成员变量的大小+ VPTR 的大小。 有时这可能不是真的,因为编译器尝试在 DWORD 边界定位成员变量。
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.
如果您想了解有关对象在运行时如何在内存中表示的详细信息,ABI(应用程序二进制接口) 规范是要查看的地方。 您需要确定您的编译器实现了哪种 ABI; 例如,GCC 版本 3.2 及更高版本实现 Itanium C++ ABI。
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.
如果您想检查特定结构的布局,
offsetof(s,member)
宏也可能有用。 它告诉您特定成员距结构基地址有多远:在典型的 32 位机器上将打印:
而在典型的 64 位机器上,它将打印
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:Would print on a typical 32-bit machine:
Whereas on a typical 64 bit machine, it would print
如果您使用的是 Microsoft Visual C++,有一个编译器选项可以告诉您对象的实际大小: /d1reportSingleClassLayout
除了 Lavavej 的这段视频之外,它没有任何文档记录 http://channel9.msdn.com/Shows/Going+Deep/C9-Lectures-斯蒂芬-T-Lavavej-高级-STL-3-of-n
If you are using Microsoft Visual C++ there is one compiler option that tells you, how large your object actually is: /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
这可能会有所帮助。
此外,类函数的表示方式与任何其他函数一样。 C++ 对函数所做的唯一魔法是修改函数名称,以使用特定类中的一组特定参数来唯一标识特定函数。
This may 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.
有一个实用程序调用
pahole
(用于 'Poke-A- HOLE'),名义上是为了研究如何填充对象布局,但通常对于可视化对象大小和布局非常有用。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.类的对象的大小等于该类的所有数据成员的大小之和。 例如,如果我
现在有一个类,如果我创建该类的一个对象,例如
s1
,那么该对象的大小将为 36 个字节:The size of an object of a class is equal to the sum of the sizes of all the data members of that class. For example if I have a class
Now, if I make an object of this class, say
s1
, then the size of this object will be 36 bytes: