内存布局C ++对象

我基本上想知道C ++如何在内存中展示对象。 所以,我听到dynamic转换简单地调整对象的指针在内存中的偏移量; 并重新解释types允许我们用这个指针做任何事情。 我真的不明白这一点。 详情将不胜感激!

每个类按照声明的顺序列出其数据成员。
编译器允许在成员之间放置填充以使访问有效(但不允许重新sorting)。

dynamic_cast<>如何工作是编译器的实现细节,而不是由标准定义的。 它将全部取决于编译器使用的ABI。

reinterpret_cast<>通过改变对象的types来工作。 唯一可以保证工作的是将一个指针指向一个void *并返回到指向类的指针将会给你相同的指针。

内存布局大部分留给实现。 关键的例外是给定访问说明符的成员variables将按照其声明的顺序。

§9.2.14

具有相同访问控制(第11章)的(非联合)类的非静态数据成员被分配,以便以后的成员在类对象中拥有更高的地址。 未指定具有不同访问控制的非静态数据成员的分配顺序(11)。 执行alignment要求可能会导致两个相邻的成员不能立即分配; 所以可能需要pipe理虚拟function(10.3)和虚拟基类(10.1)的空间。

除了成员variables之外,类或结构体需要为成员variables,基类的子对象,虚拟functionpipe理(例如虚拟表)提供空间,以及填充和alignment这些数据。 这取决于实施,但是安腾ABI规范是一个stream行的select。 海湾合作委员会和铿锵坚持它(至less到一定程度)。

http://mentorembedded.github.io/cxx-abi/abi.html#layout

Itanium ABI当然不是C ++标准的一部分,也不具有约束力。 要得到更详细的信息,你需要转向你的实现者的文档和工具。 铿锵提供了一个工具来查看类的内存布局。 举例如下:

 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); } 

在创build一个使用该类的内存布局的源文件之后,clang将显示内存布局。

 $ clang -cc1 -fdump-record-layouts layout.cpp 

Class的布局:

 *** 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的博客上可以find关于这个clangfunction的更多信息:

http://eli.thegreenplace.net/2012/12/17/dumping-ac-objects-memory-layout-with-clang/

gcc提供了一个类似的工具`-fdump-class-hierarchy'。 对于上面给出的类,它打印(除其他外):

 Class Class size=80 align=8 base size=58 base align=8 Class (0x0x141f81280) 0 vptridx=0u vptr=((& Class::_ZTV5Class) + 24u) SBase1 (0x0x141f78840) 0 primary-for Class (0x0x141f81280) SBase2 (0x0x141f788a0) 16 vptr=((& Class::_ZTV5Class) + 56u) SBase3 (0x0x141f78900) 28 VBase (0x0x141f78960) 64 virtual vptridx=8u vbaseoffset=-24 vptr=((& Class::_ZTV5Class) + 88u) 

它没有列出成员variables(或至less我不知道如何得到它),但你可以告诉他们将不得不在28和64之间的偏移量,就像在叮当声布局。

你可以看到,一个基类是primary 。 当Class被作为SBase1访问时,这就不需要调整this指针了。

gcc的等价物是:

 $ g++ -fdump-class-hierarchy -c layout.cpp 

Visual C ++的等价物是:

 cl main.cpp /c /d1reportSingleClassLayoutTest_A 

请参阅: https : //blogs.msdn.microsoft.com/vcblog/2007/05/17/diagnosing-hidden-odr-violations-in-visual-c-and-fixing-lnk2022/

答案是“这很复杂”。 dynamic转换不会简单地用偏移量调整指针; 它可能实际上检索对象内部的指针来完成它的工作。 GCC遵循为安腾devise的ABI,但实施更广泛。 你可以在这里find血腥的细节: Itanium C ++ ABI 。

如前所述,完整的细节是复杂的,痛苦的阅读,真的只对编译器开发者有用,并且在编译器之间有所不同。 基本上,每个对象都包含以下内容(通常按此顺序排列):

  1. 运行时types信息
  2. 非虚拟基础对象及其数据(可能按照声明的顺序)。
  3. 成员variables
  4. 虚拟基础对象及其数据(可能在某些DFS树search顺序中)。

这些数据可能会或可能不会被填充以使内存alignment更容易。在运行时隐藏的types信息是关于types的东西,虚拟父类的v表等等,所有这些都是编译器特定的。

在进行强制转换时, reinterpret_cast只是简单地改变指针的C ++数据types,不做其他任何事情,所以你最好确定自己在使用的时候知道自己在做什么,否则你很可能会搞砸了。 dynamic_cast与static_cast(改变指针)的作用非常相似,只是它使用运行时types信息来判断它是否可以转换为给定types,以及如何实现。 再次,所有这些都是编译器特定的。 请注意,您不能dynamic_cast一个void*因为它需要知道在哪里查找运行时types信息,以便它可以执行所有美妙的运行时检查。

这个问题已经在http://dieharddeveloper.blogspot.in/2013/07/c-memory-layout-and-process-image.html得到了解答,这里是一个摘录:在进程的地址空间的中间,那里是一个保留给共享对象的区域。; 当创build新进程时,进程pipe理器首先将可执行文件中的两个段映射到内存中。 然后解码程序的ELF头。 如果程序头指示可执行文件链接到共享库,则进程pipe理器(PM)将从程序头中提取dynamic解释程序的名称。 dynamic解释器指向包含运行时链接器代码的共享库。