1

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

例如

class Parent
{
    Child1 c1;
    Child2 c2;
};

想象一下 Child2 的构造函数需要 Child1& 并且我想将 c1 传递给 c2 构造函数。

如果我只是执行以下操作...

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

...这可能不是一件好事,因为在 c2 的初始化程序运行时可能无法构造 c1?或者在类声明中 c1 在 c2 之前是否足够好?

或者我应该明确引用 c1 构造函数(如果这不是必需的,那么这样做是否是一个好习惯以使其明确?)。例如

class Parent
{
    Child1 c1;
    Child2 c2;
};

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

5 回答 5

3

成员总是按照它们被声明的顺序构造的。因此,如果您有:

class Parent
{
    Child1 c1;
    Child2 c2;
};

您保证c1将在之前构建c2。因此,如果c2需要 a Child1&,那么这是完全明确的:

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

c1将是默认构造的,然后c2将使用绝对已经构造的c1.

于 2015-10-08T14:43:30.490 回答
1

所有这一切都没有影响。成员变量是按照它们的声明顺序构造的。句号。

这是cppreference的完整解释:

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

  1. 列表项如果构造函数是针对最派生类的,则虚基类按照其出现的顺序进行初始化,深度优先从左到右遍历基类声明(从左到右指外观在基本说明符列表中)
  2. 然后,直接基类按照从左到右的顺序初始化,因为它们出现在此类的基说明符列表中
  3. 然后,按照类定义中的声明顺序初始化非静态数据成员。
  4. 最后执行构造函数的主体
于 2015-10-08T14:43:06.727 回答
1

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

于 2015-10-08T14:43:44.107 回答
1

在 C++ 中,初始化列表中的成员不是按照您将它们放入列表中的顺序进行初始化,而是按照您声明它们的顺序进行初始化。事实上,如果你不按照声明它们的顺序初始化成员,g++ 会输出一个警告。因此,您应该注意按逻辑顺序声明成员 - 从较低级别到较高级别的对象。

于 2015-10-08T14:44:18.743 回答
1

成员数据按声明顺序构建。如果c1之前声明过c2(就像在您的示例中一样),那么它将首先被构造。

但是,您的两个示例之间存在细微差别:

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

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

如果Child1是非 POD 类类型,则两者是等价的,否则您将获得c1.

如果这对您很重要,您可以阅读default-value-initialization之间的区别。

于 2015-10-08T14:44:35.907 回答