C++构造函数很有趣 - 用自己的副本构造Foo

ano*_*non 8 c++ constructor

我有:

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)它的成员将被零初始化.基本上,您将复制零初始化对象.

但一般而言,应避免使用此类代码.:)

  • @anon:因为它的未定义行为,我认为*§12.7/ 1应该涵盖 - 在构造函数执行未定义行为的结果之前引用非静态成员. (2认同)

And*_*Dog 5

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)

但是这种特殊情况通常用于其他目的,比如廉价的字符串副本(当使用字符串类时).所以不要试图利用这种特殊情况;)