C++中的构造函数链式调用

45
构造函数链是指当一个类中存在多个构造函数(重载构造函数)时,如果其中一个构造函数试图调用另一个构造函数,则此过程称为“构造函数链”,但它在C++中不被支持。 最近我在阅读网上的材料时遇到了这段话...它是这样说的...
你可能会遇到这种情况,在这种情况下,您想编写一个成员函数将类重新初始化为默认值。因为您可能已经有一个执行此操作的构造函数,所以您可能会尝试从成员函数中调用构造函数。如上所述,在C++中,构造函数调用不能嵌套。您可以将构造函数中的代码复制到您的函数中,这将起作用,但会导致重复的代码。在这种情况下,最好的解决方案是将构造函数中的代码移动到您的新函数中,并使构造函数调用您的函数来执行数据的初始化工作。
成员函数调用构造函数是否也属于构造函数链?请在C++中对这个主题进行一些探讨。

3
我认为不是这样,文章在说某些任意成员函数(不一定是构造函数)可能希望调用构造函数来重置值之后,立刻说构造函数链接是非法的,这是一个非演绎论。但是谁知道呢,你没有引用文章或引用描述构造函数链接的地方,所以也许它已经在其他地方以一些不寻常的方式使用了。 - Steve Jessop
1
这并不违法。从成员函数中调用构造函数会创建一个临时对象,而不是从中进行调用的“this”对象。 - Amardeep AC9MF
1
@Amardeep:同意,更准确地说,在C++03中,构造函数链是“不可能的”,而不是“非法的”。简单来说,没有语法来描述它。正如你所说,按照正常定义,调用构造函数与构造函数链根本不是一回事。 - Steve Jessop
@ Steve.. 和其他感兴趣的人们,这里是链接:http://www.learncpp.com/cpp-tutorial/88-constructors-part-ii/ :) - progammer
13
C++11引入的一个特性是构造函数调用另一个构造函数,称为委派构造函数。详见链接:http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n1986.pdf。 - RoundPi
5个回答

43
C++11允许构造函数链接(部分)。这个功能称为“委托构造函数”。因此,在C++11中,您可以执行以下操作
class Foo
{
public:
    Foo(int a) : Foo() { _a = a; }
    Foo(char* b) : Foo() { _b = b; }
    Foo() { _c = 1.5; }
private:
    int _a = 0;
    char* _b = nullptr;
    double _c;
};

然而,有一个严重的限制,即调用另一个构造函数的构造函数不允许初始化任何其他成员。因此,您不能使用委托构造函数执行以下操作:
class Foo
{
public:
    Foo(int a) : Foo(), _a(a) { }
    Foo(char* b) : Foo(), _b(b) { }
    Foo() { _c = 1.5; }
private:
    int _a = 0;
    char* _b = nullptr;
    double _c;
};

MSVC++2013在后面的代码示例中给出编译错误"C3511:调用委托构造函数应该是唯一的成员初始化器"。


我想这是有道理的。调用默认构造函数应该初始化所有字段,因此再次初始化可能非常糟糕。 - lmat - Reinstate Monica
如果你认为需要这么做,那么就意味着有一个小的父类试图从Foo中分离出来。在这种情况下,FooParent会有成员变量c,而FooChild将继承它并添加ab - quant_dev

22
这段话的基本意思是:
class X
{
   void Init(params) {/*common initing code here*/ }
   X(params1) { Init(someParams); /*custom code*/ } 
   X(params2) { Init(someOtherParams); /*custom code*/ } 
};

你也不能从成员函数中调用构造函数。虽然可能看起来你已经这样做了,但那只是一种错觉:

class X
{
public:
    X(int i):i(i){}
    void f()
    {
       X(3); //this just creates a temprorary - doesn't call the ctor on this instance
    }
    int i;
};

int main()
{
    using std::cout;
    X x(4);
    cout << x.i << "\n"; //prints 4
    x.f();
    cout << x.i << "\n"; //prints 4 again
}

@ Armen .. 你是说第一次打印 4 时,实际上它正在调用构造函数,而第二次打印 4 时,尽管我感觉通过调用构造函数(从函数 void f())已经获得了结果,但确切发生的事情并不是那样。难道不是吗?但是我没有理解你在上面的代码中的第一个评论 // this just creates a temporary - doesn't call the ctor on this instance . 你能不能更清楚地解释一下呢? - progammer

4

这段文本并不是在建议您调用构造函数,而是建议您调用成员函数,这是一种正常且合法的做法。 这样做可以避免再次显式调用构造函数,并避免在构造函数和重置函数之间重复代码。

Foo::Foo() {
  Init();
}

void Foo::Reset() {
  Init();
}

void Foo::Init() {
  // ... do stuff ...
}

确实,这段文字并没有建议函数调用构造函数,但是文本中说“你可能会尝试从成员函数调用构造函数。正如提到的那样,在C++中链接构造函数调用是非法的”。这表明作者通过某种混淆的想法认为“链接构造函数调用”与“从成员函数调用构造函数”有关,无论文章是否建议这样做。当然,这可能只是编辑不良所致。 - Steve Jessop
因为您可能已经有了一个执行此操作的构造函数,所以您可能会尝试从成员函数调用构造函数。我认为这意味着应该避免成员函数调用构造函数.. !!??? - progammer
@Appy:不一定。例如,在一个名为Foo的类中,*this = Foo();可能是重置对象的完全合理的方式,尽管不一定是最有效的方式。然后函数调用构造函数,只是它不会调用它来构造this - Steve Jessop
@ Steve...感谢你的帮助.. :) 我猜这只是一些有点不靠谱的编辑。 - progammer

0

我不确定从成员函数调用构造函数是否可行,但这是一种不好的做法。将初始化代码移动到新函数是逻辑上正确的方式。

基本上说,除非你在构造,否则不要调用构造函数...


我同意Johnsyweb的观点。从另一个构造函数调用构造函数只会在其中创建一个临时局部变量,并在构造函数退出时将其删除。this对象不会受到任何影响。唯一的方法是使用放置new,而C++ FAQ反复强调这是一件可怕的事情。 - MasterMastic

-2
当我们从成员函数调用构造函数时,它将临时创建其类型的对象。 如果我们在派生类函数中调用,则所有父级构造函数也会被执行,并在函数超出范围时使用析构函数进行销毁。
在成员函数中调用构造函数并不是一个好习惯,因为它会创建每个派生类的对象。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接