C++中的对象是如何存储在内存中的?

11

C++中对象是如何存储在内存中的?

对于一个普通的类,例如:

class Object
    {
public:
    int i1;
    int i2;
    char i3;
    int i4;
private:
    };

可以使用对象的指针作为数组来访问i1,如下所示?

((Object*)&myObject)[0] === i1?

在Stack Overflow上的其他问题似乎暗示将结构体转换为指针后,对于POD类型它会指向第一个成员。如果有构造函数的类与此有何不同(如果有的话)?对于非POD类型又有何不同之处?

编辑:

因此,在内存中,上述类是否像下面这样布局?

[i1 - 4bytes][i2 - 4bytes][i3 - 1byte][padding - 3bytes][i4 - 4bytes]

你关于填充的想法可能是正确的,只要int类型占用4个字节,char类型占用1个字节。 - paercebal
5个回答

17

几乎正确。您将其转换为Object*,但忽略了获取地址。让我们改写成以下形式:

((int*)&myObject)[0] == i1
你必须非常小心地处理这种假设。就像你定义的结构一样,在你可能遇到的任何编译器中都应该是正确的。但是,对象的各种其他属性(你可能已从示例中省略)会使它变得非POD,并且可能(在依赖于编译器的方式下)使上述语句不成立。
请注意,如果你问我关于i3的情况,我不会那么快告诉你它可以工作--即使对于纯粹的POD,对齐或字节序也很容易搞砸你的代码。
无论如何,如果可能的话,你应该避免这种操作。即使现在它能正常工作,如果你(或任何其他人不理解你正在进行这种技巧)在将来更改结构顺序或添加新字段,则此技巧将在你使用它的所有位置失败,而这可能很难发现。
回答你的编辑:如果这是你整个类的定义,并且你正在使用主流编译器默认选项并在x86处理器上运行,则是的,你可能猜对了正确的内存布局。但是选择编译器、编译器选项和不同的CPU架构可能会使你的假设无效。

谢谢。将“&”符号添加到问题中。 - Dynite

5
没有虚成员和继承的类在内存中的布局与结构体相同。但是,当你开始出现继承层次时,情况会变得棘手,很难确定内存中的顺序(特别是多重继承)。
当你有虚成员时,它们在内存中有一个“vtable”,其中包含指向实际函数的指针,该函数根据类的继承层次结构创建。
最重要的是:如果可以避免,则根本不要以这种方式访问类(也不要对它们进行memset或memcpy)。如果必须这样做(为什么?),则要确保您知道类对象在内存中的确切位置,并小心避免继承。

POD类除了没有虚成员和继承外,还必须没有非公共数据成员、没有用户定义的构造函数或析构函数。它们也不能有非POD数据成员。类和结构体之间的区别是无关紧要的:重要的是POD或非POD。 - Steve Jessop
实际上,有各种非POD类,大多数编译器会将它们布局得像POD一样。除非在特定编译器的文档中有保证,否则依赖此方法是不明智的。 - Steve Jessop
你确定POD类不能使用继承吗?如果它们只从POD类型继承呢? - Joseph Garvin

3

这个技巧只适用于POD类型,与其他类型不同。标准规定这种转换对于POD类型是有效的,但不能保证非POD类型的情况。


2

这实际上取决于编译器,或者说是由编译器确定内存布局。

例如,公共、私有和受保护的成员变量可以被布置为每种访问类型都是连续的。或者,派生类可以将成员变量与超类中未使用的空间交错。

在虚拟继承中情况变得更糟,因为虚拟继承的基类可以在分配给该特定实例的内存中的任何位置上布置。

POD是不同的,因为它需要与C兼容。


0
通常情况下,重要的不是类是否有构造函数,而是类是否有任何虚方法。有关详细信息,请搜索“vtable”和“vptr”。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接