在c++中,当组合对象构造函数有依赖关系时,如何在初始化列表中强制它们的顺序

in C++, how should I enforce the order of composited object constructors in an initializer list when they have dependencies

本文关键字:列表 初始化 顺序 关系 组合 c++ 对象 构造函数 依赖      更新时间:2023-10-16

如果我有一个类,它由具有相互依赖关系的其他对象组成,(如何)我应该强制它们的构造顺序?

class Parent
{
    Child1 c1;
    Child2 c2;
};

想象Child2的构造函数需要一个Child1&我想把c1传递给c2构造函数。

如果我只是做下面的…

Parent::Parent()
    : c2(c1)
{
}

…这可能不是一件好事,因为c1可能不会在运行c2的初始化式时被构造。或者在类声明中,c1在c2之前是否足够好?

或者我应该显式地引用c1构造函数(如果没有必要,那么这样做以使其显式是一个好的实践吗?)例如

class Parent
{
    Child1 c1;
    Child2 c2;
};
Parent::Parent()
    : c1()
    : c2(c1)
{
}

成员总是按照声明的顺序构造的。所以如果你有:

class Parent
{
    Child1 c1;
    Child2 c2;
};

您可以保证c1将在c2之前构建。因此,如果c2需要Child1&,那么这是完美定义的:

Parent::Parent()
    : c2(c1)
{
}

c1将被默认构造,然后c2将用一个明确已经构造的c1来构造。

这些都不起作用。成员变量是按照声明的顺序构造的。句号。

以下是cppreference的完整解释:

成员初始化式在列表中的顺序无关紧要:初始化的实际顺序如下:

  1. 列表项如果构造函数是用于派生最多的类,则虚拟基类将按照它们在基类声明的深度优先从左到右遍历中出现的顺序进行初始化(从左到右指基类说明符列表中的出现顺序)
  2. 然后,直接基类按从左到右的顺序初始化,因为它们出现在该类的基说明符列表
  3. 然后,非静态数据成员按照类定义中的声明顺序初始化。
  4. 最后,执行构造函数体

类中的声明顺序是唯一相关的事情。编译器不遵守初始化列表中的构造顺序,实际上你可以启用一个警告来警告你这个事实(初始化列表中的顺序!=有效构造的顺序)。

在c++中,初始化列表中的成员不是按照你放入列表的顺序初始化的,而是按照你声明的顺序初始化的。事实上,如果您没有按照声明的顺序初始化成员,g++将输出警告。因此,您应该注意按照成员的逻辑顺序声明——从低级对象到高级对象。

成员数据按照声明的顺序构造。如果c1c2之前声明(就像在你的例子中一样),那么它将首先被构造。

然而,你的两个例子有一点不同:

Parent::Parent()
    // c1 is implicitly default-initialized
    : c2(c1)
{
}
Parent::Parent()
    : c1(), //c1 is value-initialized
      c2(c1)
{
}

如果Child1是非pod类类型,则两者是等效的,否则您将获得c1的不确定值。

如果这对您很重要,您可以阅读默认初始化和值初始化之间的区别。