C++ 如何确定C+中对象的大小+;?
例如,假设我有一个班临时工:C++ 如何确定C+中对象的大小+;?,c++,class,memory,size,memory-management,C++,Class,Memory,Size,Memory Management,例如,假设我有一个班临时工: class Temp { public: int function1(int foo) { return 1; } void function2(int bar) { foobar = bar; } private: int foobar; }; 当我创建一个Temp类的对象时,我将如何计算它需要多少空间,以及它在内存中是如何表示的(例如,foobar为4字节,function1为8字节,等等) 我
class Temp
{
public:
int function1(int foo) { return 1; }
void function2(int bar) { foobar = bar; }
private:
int foobar;
};
当我创建一个Temp类的对象时,我将如何计算它需要多少空间,以及它在内存中是如何表示的(例如,foobar为4字节,function1为8字节,等等)
我会给你尺寸的。最有可能的是,它是4个字节(给出了大量的假设),这仅适用于int。函数不占用每个对象的任何空间,它们只编译一次,每次使用时由编译器链接
不可能确切地说出对象布局是什么,但是,标准没有定义对象的二进制表示
对于二进制表示法,有几点需要注意,比如它们不一定是数据成员字节的总和,因为类似的事情可能会有所帮助
此外,类函数的表示方式与任何其他函数一样。C++对函数的唯一魔力是将函数名拼成一个特定的函数,在一个特定的类中用一组特定的参数唯一地标识一个特定函数。 成员函数不考虑特定类对象的大小。对象的大小仅取决于成员变量。对于包含虚拟函数的类,VPTR将添加到对象布局中。所以对象的大小基本上是成员变量的大小+VPTR的大小。有时,当编译器试图在DWORD边界上定位成员变量时,情况可能并非如此。对于一阶近似值,对象的大小是其组成数据成员大小的总和。你可以肯定它永远不会比这个小 更准确地说,编译器有权在数据成员之间插入填充空间,以确保每个数据成员满足平台的对齐要求。一些平台对对齐非常严格,而其他平台(x86)则更宽容,但如果对齐得当,性能会显著提高。因此,即使编译器优化设置也会影响对象大小
继承和虚拟函数增加了额外的复杂性。正如其他人所说,类的成员函数本身并不占用“每对象”空间,但该类接口中存在虚拟函数通常意味着存在虚拟表,本质上是一个函数指针的查找表,用于动态解析要在运行时调用的适当函数实现。虚拟表(vtbl)通常通过存储在每个对象中的指针进行访问 派生类对象还包括其基类的所有数据成员 最后,访问说明符(public、private、protected)在打包数据成员时为编译器提供一定的余地
简而言之,sizeof(myObj)或sizeof(MyClass)总是会告诉您对象的正确大小,但其结果并不总是很容易预测。如果您想了解在运行时如何在内存中表示对象的详细信息,可以查看ABI()规范。您需要查看并确定编译器实现的ABI;例如,GCC版本3.2及更高版本实现了属于类的。方法,而不是任何特定的实例化对象 除非存在虚拟方法,否则对象的大小是其非静态成员的大小加上成员之间用于对齐的可选填充的总和。成员可能会在内存中按顺序排列,但规范不能保证具有不同访问规范的节之间的顺序,也不能保证相对于超类布局的顺序 在虚拟方法存在的情况下,可能会占用额外的空间用于存储和其他RTTI信息
在大多数平台上,可执行代码位于可执行文件或库的只读
.text
(或类似名称)部分,并且从不复制到任何地方。当类Temp
有一个方法public:int function1(int)
时,Temp
元数据可能有一个指向实际实现的\u ZN4Temp9function1Ei
函数的指针(根据编译器的不同,损坏的名称可能不同),但它肯定不会包含嵌入的可执行代码。如果要检查特定结构的布局,也可以使用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
如果你使用微软Visual C++,有一个编译器选项告诉你,你的对象实际上有多大:/D1RePrimeLeCasLaySudio
除了Lavavej的这段视频之外,它没有文档记录。一个类的对象的大小等于该类所有数据成员的大小之和。例如,如果我有一门课
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)]
有一个实用程序调用
pahole
(for),它名义上是为了研究对象布局是如何填充的,但是对于可视化对象大小和布局来说非常有用。我一直在想这类事情,所以我决定想出一个完整的答案。这是关于你可能期望的,而且是可预测的(耶)!因此,通过下面的信息,您应该能够预测类的大小
使用Visual Studio Community 2017(版本15.2),在发布模式下禁用所有优化并关闭RTTI(),我确定了以下内容:
简短回答: 首先:
- 在32(x86)位中,
字节==4
- 在64(x64)位中,
字节==8
- 当我说“虚拟类继承”时,我的意思是:<代码>类子类:虚拟公共PAR
[20(name)+2(rollno)+2(admno)+4(marks)+4(tmarks)+4(percentage)]
#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"); }
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)
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)