【发布时间】:2019-02-01 18:08:09
【问题描述】:
假设我们有一个类B,它有一个member,默认初始化为42。此类知道如何打印其member 的值(它在构造函数中这样做):
struct B
{
B() : member(42) { printMember(); }
void printMember() const { std::cout << "value: " << member << std::endl; }
int member;
};
然后我们添加一个类A,它接收一个对B 的常量引用并要求B 打印它的值:
struct A
{
A(const B& b) { b.printMember(); }
};
最后我们添加另一个类Aggregate,它聚合了A 和B。棘手的部分是A 类型的对象a 在对象b 类型B 之前声明,但随后a 使用(尚未有效?)对b 的引用进行初始化:
struct Aggregate
{
A a;
B b;
Aggregate() : a(b) { }
};
考虑创建Aggregate 的输出(我在A 和B 的构造函数和析构函数中添加了一些日志记录)(Try it online!):
a c'tor
value: 0
b c'tor
value: 42
b d'tor
a d'tor
我是否可以假设使用 b 的(尚未有效的)实例来初始化 a 是无效的,因此这是未定义的行为?
我知道初始化顺序。这就是让我挣扎的原因。我知道 b 尚未构建,但我也认为知道 b 的未来地址甚至可以在 b 构建之前确定。因此我假设可能有一些我不知道的规则允许编译器在b 的构造之前默认初始化bs 成员或类似的东西。 (如果第一个打印出来的值看起来是随机的而不是0(默认值int),那会更明显。
This answer帮助我明白了我需要区分
- 绑定一个引用到一个未初始化的对象(这是有效的)和
- 通过引用访问一个未初始化的对象(未定义)
【问题讨论】:
-
如果您在类定义中切换它们的顺序,它将具有更好的定义行为。或者,不要使用引用来访问对象(有关相关问题,请参阅此处stackoverflow.com/questions/50020255/…)。
-
我知道如何“修复”它。我不确定的是,它是应该被修复(因为它很丑)还是必须被修复(因为它是未定义的行为,因此被破坏了);)跨度>
-
您显示的内容已损坏。另一个问题中的例子不是。这是一个标准的雷区
-
您在初始化之前使用对象。为什么你认为你不应该修复它?
-
b无效。 对b的引用 是有效的,但它是对尚未构造的对象的引用;它可以以某些有限的方式使用,但不能以您使用它的方式。
标签: c++ constructor initialization undefined-behavior default-constructor