我有:
class Foo;
class Bar {
Foo foo;
Bar(): foo(foo) {};
}
Bar bar;
Run Code Online (Sandbox Code Playgroud)
在这一点上,是
bar.foo // <--- how is this initialized?
Run Code Online (Sandbox Code Playgroud)
[这个问题来自一个错误的重新计数指针实现; 我本可以发誓我确保每个指针都指向非空的东西; 但我最终得到了一个指向NULL的指针.
GMa*_*ckG 12
foo 一旦你进入了构造函数的主体就完全初始化了(这是保证的一般情况;特别是在初始化列表中完成初始化之后.)
在您的情况下,您是从非构造对象进行复制构造.这导致未定义的行为,根据§12.7/ 1(谢谢你,gf):
对于非POD类类型的对象(第9节),在构造函数开始执行之前和析构函数完成执行之后,引用该对象的任何非静态成员或基类会导致未定义的行为.
实际上,它给出了这个例子:
struct W { int j; };
struct X : public virtual W { };
struct Y {
int *p;
X x;
Y() : p(&x.j) // undefined, x is not yet constructed
{ }
};
Run Code Online (Sandbox Code Playgroud)
请注意,在编译器不要求给出不确定的行为诊断,每§1.4/ 1.虽然我认为我们都同意它会很好,但它根本不是编译器实现者需要担心的事情.
查尔斯指出了各种各样的漏洞.如果Bar有静态存储且if Foo是POD类型,则在此代码运行时将初始化它.静态存储变量在其他初始化运行之前进行零初始化.
这意味着什么Foo,只要它不需要运行构造函数来初始化(即,是POD)它的成员将被零初始化.基本上,您将复制零初始化对象.
但一般而言,应避免使用此类代码.:)
Bar(): foo(foo) {};
Run Code Online (Sandbox Code Playgroud)
这将调用复制构造函数foo,从而从非初始化对象复制构造.这将导致未定义的行为,除非您已实现处理该特定情况的复制构造函数,例如:
class Foo
{
public:
Foo()
{
std::cout << "Foo()";
}
Foo(const Foo& from)
{
if(this == &from) std::cout << "special case";
else std::cout << "other case";
}
};
Run Code Online (Sandbox Code Playgroud)
但是这种特殊情况通常用于其他目的,比如廉价的字符串副本(当使用字符串类时).所以不要试图利用这种特殊情况;)