在 C++ 中,当它们具有依赖项时,我应该如何在初始化列表中强制执行组合对象构造函数的顺序
in C++, how should I enforce the order of composited object constructors in an initializer list when they have dependencies
如果我有一个 class 组合具有相互依赖性的其他对象,我应该(如何)执行它们的构造顺序?
例如
class Parent
{
Child1 c1;
Child2 c2;
};
假设 Child2 的构造函数需要一个 Child1&,我想将 c1 传递给 c2 构造函数。
如果我只执行以下操作...
Parent::Parent()
: c2(c1)
{
}
...这可能不是好事,因为 c2 的初始值设定项为 运行 时 c1 可能尚未构造?或者在 class 声明中 c1 出现在 c2 之前是否足够好?
或者我应该显式地引用 c1 构造函数(如果这不是必需的,那么这样做以使其显式化是一个好习惯吗?)。例如
class Parent
{
Child1 c1;
Child2 c2;
};
Parent::Parent()
: c1()
: c2(c1)
{
}
这一切都没有效果。成员变量按照声明的顺序构造。句号。
这是来自 cppreference 的完整解释:
The order of member initializers in the list is irrelevant: the actual order of initialization is as follows:
- List item
If the constructor is for the most-derived class, virtual base classes are initialized in the order in which they appear in depth-first left-to-right traversal of the base class declarations (left-to-right refers to the appearance in base-specifier lists)
- Then, direct base classes are initialized in left-to-right order as they appear in this class's base-specifier list
- Then, non-static data members are initialized in order of declaration in the class definition.
- Finally, the body of the constructor is executed
成员总是按照声明的顺序构造。所以如果你有:
class Parent
{
Child1 c1;
Child2 c2;
};
你保证 c1
将在 c2
之前构建。所以,如果 c2
需要一个 Child1&
,那么这是完全明确的:
Parent::Parent()
: c2(c1)
{
}
c1
将被默认构造,然后 c2
将使用一个明确已经构造的 c1
来构造。
class 中的声明顺序是唯一相关的事情。编译器不遵守初始化列表中的构造顺序,实际上您可以启用警告以警告您这一事实(初始化列表中的顺序!=有效构造的顺序)。
在 C++ 中,初始化列表中的成员不是按照您将它们放入列表中的顺序初始化的,而是按照您声明它们的顺序初始化的。事实上,如果你没有按照你声明的顺序初始化成员,g++ 会输出一个警告。因此,您应该注意 声明 成员的逻辑顺序 - 从较低级别到较高级别的对象。
成员数据按声明顺序构造。如果 c1
在 c2
之前声明(就像在您的示例中一样),那么它将首先构造。
但是,您的两个示例之间存在细微差别:
Parent::Parent()
// c1 is implicitly default-initialized
: c2(c1)
{
}
Parent::Parent()
: c1(), //c1 is value-initialized
c2(c1)
{
}
如果 Child1
是非 POD class 类型,则两者是等价的,否则您将获得 c1
.
的不确定值
如果这对您很重要,您可以阅读 default- and value-initialization.
之间的区别
如果我有一个 class 组合具有相互依赖性的其他对象,我应该(如何)执行它们的构造顺序?
例如
class Parent
{
Child1 c1;
Child2 c2;
};
假设 Child2 的构造函数需要一个 Child1&,我想将 c1 传递给 c2 构造函数。
如果我只执行以下操作...
Parent::Parent()
: c2(c1)
{
}
...这可能不是好事,因为 c2 的初始值设定项为 运行 时 c1 可能尚未构造?或者在 class 声明中 c1 出现在 c2 之前是否足够好?
或者我应该显式地引用 c1 构造函数(如果这不是必需的,那么这样做以使其显式化是一个好习惯吗?)。例如
class Parent
{
Child1 c1;
Child2 c2;
};
Parent::Parent()
: c1()
: c2(c1)
{
}
这一切都没有效果。成员变量按照声明的顺序构造。句号。
这是来自 cppreference 的完整解释:
The order of member initializers in the list is irrelevant: the actual order of initialization is as follows:
- List item If the constructor is for the most-derived class, virtual base classes are initialized in the order in which they appear in depth-first left-to-right traversal of the base class declarations (left-to-right refers to the appearance in base-specifier lists)
- Then, direct base classes are initialized in left-to-right order as they appear in this class's base-specifier list
- Then, non-static data members are initialized in order of declaration in the class definition.
- Finally, the body of the constructor is executed
成员总是按照声明的顺序构造。所以如果你有:
class Parent
{
Child1 c1;
Child2 c2;
};
你保证 c1
将在 c2
之前构建。所以,如果 c2
需要一个 Child1&
,那么这是完全明确的:
Parent::Parent()
: c2(c1)
{
}
c1
将被默认构造,然后 c2
将使用一个明确已经构造的 c1
来构造。
class 中的声明顺序是唯一相关的事情。编译器不遵守初始化列表中的构造顺序,实际上您可以启用警告以警告您这一事实(初始化列表中的顺序!=有效构造的顺序)。
在 C++ 中,初始化列表中的成员不是按照您将它们放入列表中的顺序初始化的,而是按照您声明它们的顺序初始化的。事实上,如果你没有按照你声明的顺序初始化成员,g++ 会输出一个警告。因此,您应该注意 声明 成员的逻辑顺序 - 从较低级别到较高级别的对象。
成员数据按声明顺序构造。如果 c1
在 c2
之前声明(就像在您的示例中一样),那么它将首先构造。
但是,您的两个示例之间存在细微差别:
Parent::Parent()
// c1 is implicitly default-initialized
: c2(c1)
{
}
Parent::Parent()
: c1(), //c1 is value-initialized
c2(c1)
{
}
如果 Child1
是非 POD class 类型,则两者是等价的,否则您将获得 c1
.
如果这对您很重要,您可以阅读 default- and value-initialization.
之间的区别