如何为类的私有成员和公共成员分配内存

11

在一个类中,私有成员和公共成员是否分别分配在不同的内存中,还是所有成员按照它们定义的顺序分配?

例如,

class A { 

private:  
    int a1;
    int a2:3;  
public:  
    int z;  
    int a3:2;  
    int a4:5;  
private:
    int a5:2;
}

内存分配时,a1a2a5是否被合并在一起?还是只是 a1a2a3a4a5单独分配?

如果合并发生,可能会改变位字段类的大小。


2
为什么你需要知道那个?Java 是高级语言,不需要关心这个问题,而在 C/C++ 中则不同。 - Jonatan Cloutier
2
等等,为什么要加Java标签? - Ben
现在很清楚了! - Jonatan Cloutier
@JonatanCloutier 如果说私有成员被合并在一起,那么上面的例子中类的大小不会发生变化吗? - Aragon
我只考虑了Java方面,抱歉不能确定C ++的情况。 - Jonatan Cloutier
@user:是的,在上面的例子中它可能会有所不同。例如,将所有位域打包到类的64位整数成员中完全符合规范,而不同的编译器可能会选择不同的答案。请参见我的回答中的标准文献引用。 - Billy ONeal
2个回答

10
  • 在给定的可访问性块内,成员的顺序被保留,但在可访问性块之间,成员顺序在C++03中是未指定的。这意味着a1、a2、a5、z、a3、a4将是你上面例子中的一个有效顺序。
  • 在C++11中,这一点得到了加强,相同可访问性的成员必须按照声明的顺序放置。(例如,在您的示例中,禁止a5 a1 a2 f a3 a4,因为a5在您的示例中是在a1和a2之后声明的)。不同可访问性的成员之间的顺序是未指定的。
  • 编译器可以在任何成员之间插入填充(例如,为了保持对齐)
  • 位域的表示是未指定的。它们可以以任何顺序放置,并且不遵守先前的任何规则。因为不能取位域的地址,所以没有机制来观察这一点。

具体的标准参考资料(重点在于我):

C++03 9.2 [class.mem]/12:

一个(非联合)类中未使用访问说明符声明的非静态数据成员被分配,以便稍后的成员在类对象内具有更高的地址。通过访问说明符分隔的非静态数据成员的分配顺序是未指定的(11.1)。实现对齐要求可能导致两个相邻的成员不会立即相邻分配; 对于管理虚函数(10.3)和虚基类(10.1)所需的空间也可能如此。根据N3376(C ++ 11草案之后的第一篇文章)9.2 [class.mem] / 13:一个(非联合)类中具有相同访问控制(第11条)的非静态数据成员被分配,以便稍后的成员在类对象内具有更高的地址。访问控制不同的非静态数据成员的分配顺序是未指定的。实现对齐要求可能导致两个相邻的成员不会立即相邻分配; 对于管理虚函数(10.3)和虚基类(10.1)所需的空间也可能如此。

N3376 9.6 [class.bit]/1:

在类对象中分配位字段是实现定义的。位字段的对齐方式也是实现定义的。 [...]

/3:

不能将取地址运算符&应用于位字段,因此没有指向位字段的指针。 [...]


5

C++ 答案:

可能会有遗漏,如有请在评论中指出。

在 C++ 中,对象的布局基于类的类型。在标准中,我相信只有少数几种类可以保证成员的排序。

POD 或 Plain Old Data 类 / 结构与它们的出现顺序完全一样。每个成员都跟在上面声明的成员后面,并添加填充。POD 类/结构是不包含任何访问修饰符、没有任何用户定义的构造函数/析构函数、没有静态声明成员,只包含 POD 成员类型的类/结构。

例如:

struct POD {
    int x;
    int y;
    int z;
};

大多数其他类型的类并没有定义成员在内存中的布局方式。如果一个类/结构有多个访问修饰符,编译器可以自由地放置成员。同样,对于大多数不涉及与硬件工作的情况,C++应该与Java类似,您不需要过多关心它的布局。
以下内容是在删除java标签之前添加的,但仍供参考。 Java答案: Java不能保证对象在内存中的布局方式,您不应该关心它。JVM可以自由地在内存中布局对象。这意味着对象甚至不必在连续的内存中。
现在我不知道任何JVM是否会这样做。更好的猜测是它以类似于C++的方式布置对象。其中每个父类成员都放在子成员之前,并且每个类中的成员都根据大小连续布置,并需要填充。
如果您想检查特定对象在内存中的布局方式,则可以使用反射和sun.misc.Unsafe类。

请注意编译器完全可以在访问块之间重新排序内容。例如,如果编译器想出于某种原因首先放置所有私有成员,它是允许这样做的。 - Billy ONeal
@BillyONeal 我相信我在C++答案中已经涵盖了那部分内容。如果有任何措辞不当的地方,我会接受更好的措辞建议。 - Smith_61
POD与可访问性实际上没有任何关系。POD不涉及类的布局。POD类具有这样的特征,它们是memcpy-able(std::is_trivially_copyable),并且只要分配了存储空间(没有构造函数调用),就被认为已构造。 - Billy ONeal
@BillyONeal 我认为POD类型不能使用访问修饰符。使用它们会使其不再是POD类型。不过,我应该添加一个更好的POD类型定义。 - Smith_61
啊,我忘了。为了符合标准布局(POD的要求之一),所有访问控制必须相同。因此,例如,如果所有非静态数据成员都是私有的,那就没问题了。但是混合使用公共和私有成员是不行的。 - Billy ONeal

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