内存布局C++;物体 我基本上想知道C++如何在内存中绘制对象。所以,我听说动态强制转换只是用偏移量调整对象在内存中的指针;重新解释允许我们用这个指针做任何事情。我真的不明白。详情将不胜感激 答案是“很复杂”。动态强制转换不只是用偏移量调整指针;它实际上可能检索对象内部的指针以完成其工作。GCC遵循为安腾设计的ABI,但实现范围更广。您可以在这里找到血淋淋的细节:。
每个类都按照声明的顺序排列其数据成员。内存布局C++;物体 我基本上想知道C++如何在内存中绘制对象。所以,我听说动态强制转换只是用偏移量调整对象在内存中的指针;重新解释允许我们用这个指针做任何事情。我真的不明白。详情将不胜感激 答案是“很复杂”。动态强制转换不只是用偏移量调整指针;它实际上可能检索对象内部的指针以完成其工作。GCC遵循为安腾设计的ABI,但实现范围更广。您可以在这里找到血淋淋的细节:。,c++,memory,object,C++,Memory,Object,每个类都按照声明的顺序排列其数据成员。 允许编译器在成员之间放置填充以提高访问效率(但不允许重新排序) dynamic\u cast的工作原理是一个编译器实现细节,而不是由标准定义的。这一切都取决于编译器使用的ABI reinterpret\u cast只需更改对象的类型即可工作。唯一可以保证有效的是,将指针转换为void*并返回到相同的类指针,将得到相同的指针。如前所述,完整的细节非常复杂,阅读起来很痛苦,并且只对编译器开发人员有用,并且在编译器之间有所不同。基本上,每个对象包含以下内容(通常
允许编译器在成员之间放置填充以提高访问效率(但不允许重新排序)
dynamic\u cast
的工作原理是一个编译器实现细节,而不是由标准定义的。这一切都取决于编译器使用的ABI
reinterpret\u cast
只需更改对象的类型即可工作。唯一可以保证有效的是,将指针转换为void*并返回到相同的类指针,将得到相同的指针。如前所述,完整的细节非常复杂,阅读起来很痛苦,并且只对编译器开发人员有用,并且在编译器之间有所不同。基本上,每个对象包含以下内容(通常按此顺序排列):
当涉及到转换时,
adynamic\u cast
,因为它需要知道在哪里可以找到运行时类型信息,这样它就可以进行所有出色的运行时检查。void*
在进程的地址空间的中间,有一个区域被保留为共享对象。创建新进程时,进程管理器首先将可执行文件中的两个段映射到内存中。然后解码程序的ELF头。如果程序头指示可执行文件链接到共享库,则process manager(PM)将从程序头中提取动态解释器的名称。动态解释器指向包含运行时链接器代码的共享库 内存布局主要由实现完成。关键的例外是,给定访问说明符的成员变量将按声明顺序排列 §9.2.14 具有相同访问权限的(非联合)类的非静态数据成员 控制权(第11条)的分配是为了使后来的成员拥有更高的控制权 类对象中的地址。非静态资源的分配顺序 未指定具有不同访问控制的数据成员(11)。 实现一致性要求可能会导致两个相邻的成员 不在彼此之后立即分配;也可能 管理虚拟功能的空间要求(10.3)和 虚拟基类(10.1) 除成员变量外,类或结构需要为成员变量、基类的子对象、虚拟函数管理(如虚拟表)以及这些数据的填充和对齐提供空间。这取决于实现,但安腾ABI规范是一种流行的选择。gcc和clang都遵守它(至少在一定程度上) P. Itabi ABI当然不是C++标准的一部分,不具有约束力。要获得更详细的信息,您需要查阅实施者的文档和工具。clang提供了一个查看类的内存布局的工具。例如,以下内容:
class VBase {
virtual void corge();
int j;
};
class SBase1 {
virtual void grault();
int k;
};
class SBase2 {
virtual void grault();
int k;
};
class SBase3 {
void grault();
int k;
};
class Class : public SBase1, SBase2, SBase3, virtual VBase {
public:
void bar();
virtual void baz();
// virtual member function templates not allowed, thinking about memory
// layout and vtables will tell you why
// template<typename T>
// virtual void quux();
private:
int i;
char c;
public:
float f;
private:
double d;
public:
short s;
};
class Derived : public Class {
virtual void qux();
};
int main() {
return sizeof(Derived);
}
类的布局
:
*** Dumping AST Record Layout
0 | class Class
0 | class SBase1 (primary base)
0 | (SBase1 vtable pointer)
8 | int k
16 | class SBase2 (base)
16 | (SBase2 vtable pointer)
24 | int k
28 | class SBase3 (base)
28 | int k
32 | int i
36 | char c
40 | float f
48 | double d
56 | short s
64 | class VBase (virtual base)
64 | (VBase vtable pointer)
72 | int j
| [sizeof=80, dsize=76, align=8
| nvsize=58, nvalign=8]
更多关于这个叮当作响的功能可以在Eli Bendersky的博客上找到:
gcc提供了一个类似的工具,`-fdump类层次结构'。对于上面给出的类,它打印(除其他外):
它没有逐项列出成员变量(或者至少我不知道如何使用它),但是您可以知道它们必须在偏移量28和64之间,就像在clang布局中一样
您可以看到一个基类被单独指定为primary
。当作为SBase1
访问Class
时,这样就不需要调整此
指针
gcc的等效值为:
$ g++ -fdump-class-hierarchy -c layout.cpp
Visual C++的等价性是:
cl main.cpp /c /d1reportSingleClassLayoutTest_A
请参阅:您的第一点并不完全正确。您唯一能保证的是,同一访问块中的成员将具有已定义的顺序。如果你想把这一点发挥到极致,你可以说,即使访问权限相同,订单也不再有保证。@Richard。我不确定我是否理解你。编译器不允许对元素重新排序(这是为了与C向后兼容)。什么是访问块。您能告诉我您从标准中获取信息的正确部分吗?访问块(实际上是访问说明符)是类中的
公共:
、私有:
、和受保护:
。我发现这篇文章:非常有用,这就是我想要的
$ g++ -fdump-class-hierarchy -c layout.cpp
cl main.cpp /c /d1reportSingleClassLayoutTest_A