C ++类:是否可以在构造函数体中选择成员的构造函数? (没有双重初始化成员)

时间:2015-06-12 02:11:12

标签: c++ parameters constructor default-constructor initialization-list

考虑一下:

class Foo {
    private:
        Bar x;

    public:
        Foo(int a) { // no initialization here since constructor is dependent on a following if-block
            if (a==0) x=Bar(12,'a', 34);  // some (int, char, int) constructor of Bar
            else x=Bar(13); // (int) constructor of Bar
      }
}

这应该做的是检查参数a的值,并使用某个构造函数和某些参数初始化Bar x,具体取决于值。但问题是,这当然是由编译器读取的

        Foo(int a): Bar() { // !
            if (a==0) x=Bar(12,'a', 34);  // some Bar(int, char, int) constructor of Bar
            else x=Bar(13); // Bar(int) constructor of Bar
        }

编译器将Bar()添加到初始化列表中,因为我省略了它。它现在被双重初始化,一次使用其()构造函数,一次在函数体中。但是,如果初始化Bar(即使使用其默认构造函数)非常昂贵(关于性能)并且我不能或不想进行这种双重初始化会怎样呢?

如何在实际的构造函数体中初始化Bar x? 如果不可能,我将如何最好地解决这个问题?

除此之外:为什么C ++以这种方式设计?为什么它强制在实际构造函数体之前初始化成员?

6 个答案:

答案 0 :(得分:2)

不,你不能。保证成员在ctor机构中进行初始化。

整个初始化顺序定义如下:

  • 首先,派生程度最高的类的构造函数调用虚拟基类子对象的构造函数。虚拟基类以深度优先,左直接顺序初始化。
  • 接下来,直接基类子对象按照它们在类定义中声明的顺序构建。
  • 接下来,(非静态)成员子对象按它们的顺序构建 在类定义中声明。
  • 最后,执行构造函数的主体。

答案 1 :(得分:2)

您可以将x设为std::unique_ptr<Bar>,然后根据需要在ctor中进行分配。

答案 2 :(得分:2)

我认为你问的是错误的问题,而不是试图禁止初始化,你应该这样做,即拼写你的ctor:

Foo(int a) : x((a==0) ? Bar(12,'a', 34) : Bar(13)) {}

这不会导致任何副本或移动(请参阅here),并且不会产生任何惯用。

答案 3 :(得分:1)

C ++就是这样设计的,因此构造函数实际上构造并初始化了东西,而不是让你自己在脚下射击。

尽管如此,自从C ++ 11以来,你可以使用后门徒步技术:除非你明确地构建它们,否则匿名联盟的成员不会被构造。代码是:

class Foo
{
    union 
    {
        Bar x;
    };

public:
    Foo(int a)
    {
        if (a==0) 
            new(&x) Bar(12,'a', 34);
        else
            new(&x) Bar(13);
    }

    ~Foo()
    {
         x.~Bar();
    }
};

要明确这应该是最后的手段。编译器生成的复制构造函数,移动构造函数等被定义为已删除,因此如果您希望这些操作可用,则需要同时实现它们。

答案 4 :(得分:1)

与匿名联合解决方案相比,内存明显更加昂贵,但更不容易出错,并且不会产生额外的复制/移动或动态分配。

class Foo {
    private:
        std::experimental::optional<Bar> x;

    public:
        Foo(int a) {
            if (a==0) x.emplace(12,'a', 34);  // some (int, char, int) constructor of Bar
            else x.emplace(13); // (int) constructor of Bar
      }
};

此外,在这种情况下,x将像指针一样使用:x->some_Bar_function()some_function_taking_a_Bar(*x)

答案 5 :(得分:0)

以下是使用委托构造函数的解决方案:

class Foo
{
    Bar b;
    Foo( Bar b ): b(std::move(b)) { }

public:
    Foo(int a): Foo( a ? Bar(13) : Bar(12, 'a', 34) ) {}
};

在委派期间创建所需对象,然后调用采用Bar的构造函数。如果你想允许任何人从Bar构造一个Foo,那么你可以将该构造函数公开。