例如,假设我有一个类Temp:
class Temp
{
public:
int function1(int foo) { return 1; }
void function2(int bar) { foobar = bar; }
private:
int foobar;
};
当我创建Temp类的对象时,我如何计算它需要多少空间,以及它如何在内存中表示(例如| foobar | 4个字节| function1 | 8的8个字节|)
答案 0 :(得分:60)
对于一阶近似,对象的大小是其组成数据成员的大小的总和。你可以肯定它永远不会小于此。
更准确地说,编译器有权在数据成员之间插入填充空间,以确保每个数据成员满足平台的对齐要求。一些平台对齐非常严格,而其他平台(x86)更宽容,但通过适当的对齐将表现得更好。因此,即使编译器优化设置也会影响对象大小。
继承和虚函数会增加额外的复杂性。正如其他人所说,类的成员函数本身不占用“每个对象”空间,但该类接口中虚函数的存在通常意味着存在虚拟表,本质上是一个函数指针的查找表,用于动态解析正确的函数实现,以便在运行时调用。通常通过存储在每个对象中的指针来访问虚拟表(vtbl)。
派生类对象还包括其基类的所有数据成员。
最后,访问说明符(public,private,protected)授予编译器对数据成员打包的某些余地。
简短的回答是sizeof(myObj)或sizeof(MyClass)将始终告诉您对象的正确大小,但其结果并不总是很容易预测。
答案 1 :(得分:18)
sizeof(Temp)
会给你大小。最有可能的是,它是4个字节(给出了很多假设)并且仅适用于int。这些函数不会占用每个对象的任何空间,它们被编译一次,并在每次使用时由编译器链接。
不可能确切地说出对象布局是什么,但是,标准没有定义对象的二进制表示。
对于二进制表示,有一些事情要注意,因为它们不一定是数据成员的字节总和,因为像structure padding
这样的事情答案 2 :(得分:8)
如果需要有关如何在运行时在内存中表示对象的详细信息,则可以查看ABI(Application Binary Interface)规范。您需要查看确定编译器实现的ABI;例如,GCC 3.2及更高版本实现了Itanium C++ ABI。
答案 3 :(得分:8)
我一直想知道这种事情,所以我决定想出一个完整的答案。这是关于你可能期望的,它是可预测的(耶!)!因此,通过以下信息,您应该能够预测班级的大小。
使用Visual Studio Community 2017(版本15.2),在发布模式下禁用所有优化并关闭RTTI(Run-time Type Information),我已确定以下内容:
简短回答:
首先:
<size of pointer> == 4
字节<size of pointer> == 8
字节class ChildClass: virtual public ParentClass
现在,我的发现是:
<size of variable>
bytes <size of variables>
bytes <size of pointer>
个字节<size of pointer>
个字节<size of pointer>
个字节<size of pointer>
个字节添加到总数中,包装所有成员变量都以<size of pointer>
字节为增量覆盖<total size of member variables>
所需的增量 - 是的,你读得正确...(看看我对结论中发生了什么的猜测/强> ...)注意我甚至尝试过使用function()cout一些文本,创建类的实例,并调用该函数;它不会改变函数类的大小(它不是优化)!我有些惊讶,但它确实有意义:成员函数不会改变,所以它们可以存储在类本身的外部。
<强>结论:强>
<size of pointer>
个字节,向所述类添加<size of pointer>
个字节。当然,这个vtable每个类只能存在一次(无论是它还是不存在)。<size of pointer>
个字节,甚至如果它不需要消耗那么多内存,我猜是因为它为每个<size of pointer>
个字节的内存添加了一个vtable“helper block”或者...... 答案很长:
我使用此代码确定了所有这些:
#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)
如果您需要有关多重继承的信息,去了解您的自我! -.- 强>
答案 4 :(得分:6)
方法属于类,而不是任何特定的实例化对象。
除非有虚方法,否则对象的大小是其非静态成员大小的总和,以及成员之间用于对齐的可选填充。成员可能会在内存中按顺序排列,但规范并不保证具有不同访问规范的部分之间的排序,也不保证相对于超类的布局进行排序。
如果存在虚拟方法,vtable和其他RTTI信息可能会有额外的空间。
在大多数平台上,可执行代码都位于可执行文件或库的只读.text
(或类似名称)部分,并且永远不会复制到任何位置。当class Temp
具有方法public: int function1(int)
时,Temp
元数据可能具有指向实际实现的_ZN4Temp9function1Ei
(受损编译名称可能因编译器而异)的指针,但是当然它永远不会包含嵌入的可执行代码。
答案 5 :(得分:4)
成员函数不考虑特定类的对象的大小。对象的大小仅取决于成员变量。对于包含虚函数的类,VPTR将添加到对象布局中。因此,对象的大小基本上是成员变量的大小+ VPTR的大小。有时这可能不正确,因为编译器试图在DWORD边界定位成员变量。
答案 6 :(得分:3)
如果您使用的是Microsoft Visual C ++,则有一个编译器选项可以告诉您实际对象的大小:/ d1reportSingleClassLayout
除了Lavavej http://channel9.msdn.com/Shows/Going+Deep/C9-Lectures-Stephan-T-Lavavej-Advanced-STL-3-of-n
的视频外,它没有记录答案 7 :(得分:2)
如果要检查特定结构的布局,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;
}
将在典型的32位计算机上打印:
foo->a is 0 bytes into a foo
foo->b is 4 bytes into a foo
在典型的64位机器上,它会打印
foo->a is 0 bytes into a foo
foo->b is 8 bytes into a foo
答案 8 :(得分:0)
This可能会有所帮助。
此外,类功能与任何其他功能一样。 C ++对该函数的唯一作用是破坏函数名称,以使用特定类中的特定参数集唯一地标识特定函数。
答案 9 :(得分:0)
实用程序调用{{1}}(适用于'Poke-A-HOLE')名义上用于研究对象布局如何填充,但通常可视化对象大小和布局。
答案 10 :(得分:-1)
类的对象大小等于该类的所有数据成员的大小总和。例如,如果我有一个班级
class student
{
private:
char name[20];
int rollno, admno;
float marks;
public:
float tmarks, percentage;
void getdata();
void putdata();
};
现在,如果我创建了这个类的对象,比如s1
,那么这个对象的大小将是36个字节:
[20(name)+2(rollno)+2(admno)+4(marks)+4(tmarks)+4(percentage)]