我有:
class Foo;
class Bar {
Foo foo;
Bar(): foo(foo) {};
}
Bar bar;
此时,是
bar.foo // <--- how is this initialized?
[这个问题来自一个错误的引用计数指针实现;我本可以发誓我确保每个指针都指向非空的东西;但我最终得到了一个指向NULL的指针。]
答案 0 :(得分: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
{ }
};
注意,根据§1.4/ 1,编译器不需要对未定义的行为进行诊断。虽然我认为我们都同意它会很好,但它根本不是编译器实现者需要担心的事情。
查尔斯指出了各种各样的漏洞。如果Bar
具有静态存储且Foo
是POD类型,则在此代码运行时将初始化。静态存储变量在其他初始化运行之前进行零初始化。
这意味着无论Foo
是什么,只要它不需要运行构造函数来进行初始化(即,是POD),它的成员将被零初始化。基本上,你将复制零初始化对象。
一般而言,应避免使用此类代码。 :)
答案 1 :(得分:5)
Bar(): foo(foo) {};
这将调用foo
的复制构造函数,从而从非初始化对象复制构造。这将导致未定义的行为,除非您已实现处理该特定情况的复制构造函数,例如:
class Foo
{
public:
Foo()
{
std::cout << "Foo()";
}
Foo(const Foo& from)
{
if(this == &from) std::cout << "special case";
else std::cout << "other case";
}
};
但是这种特殊情况通常用于其他目的,比如廉价的字符串副本(当使用字符串类时)。所以不要试图利用这种特殊情况;)
答案 2 :(得分:3)
稍微扩展的代码版本似乎表明不,foo
永远不会被初始化;你似乎有未定义的行为。在此示例中,永远不会打印"Foo()"
,表示没有构造Foo
的实例:
#include <iostream>
class Foo {
public:
Foo() { std::cerr << "Foo()"; }
};
class Bar {
public:
Foo foo;
Bar(): foo(foo) {};
};
int main() {
Bar bar;
}
答案 3 :(得分:0)
不是Foo使用默认的内在构造函数,初始化列表会自动调用默认构造函数来初始化对象吗?