我可以在C++中从另一个构造函数(构造函数链接)调用构造函数吗?

Sto*_*net 879 c++ constructor

作为C#开发人员,我习惯于运行构造函数:

class Test {
    public Test() {
        DoSomething();
    }

    public Test(int count) : this() {
        DoSomethingWithCount(count);
    }

    public Test(int count, string name) : this(count) {
        DoSomethingWithName(name);
    }
}
Run Code Online (Sandbox Code Playgroud)

有没有办法在C++中执行此操作?

我尝试调用类名并使用'this'关键字,但都失败了.

Joh*_*dol 1175

C++ 11:是的!

C++ 11及更高版本具有相同的功能(称为委托构造函数).

语法与C#略有不同:

class Foo {
public: 
  Foo(char x, int y) {}
  Foo(int y) : Foo('a', y) {}
};
Run Code Online (Sandbox Code Playgroud)

C++ 03:没有

不幸的是,在C++ 03中没有办法做到这一点,但有两种模拟方法:

  1. 您可以通过默认参数组合两个(或更多)构造函数:

    class Foo {
    public:
      Foo(char x, int y=0);  // combines two constructors (char) and (char, int)
      // ...
    };
    
    Run Code Online (Sandbox Code Playgroud)
  2. 使用init方法共享公共代码:

    class Foo {
    public:
      Foo(char x);
      Foo(char x, int y);
      // ...
    private:
      void init(char x, int y);
    };
    
    Foo::Foo(char x)
    {
      init(x, int(x) + 7);
      // ...
    }
    
    Foo::Foo(char x, int y)
    {
      init(x, y);
      // ...
    }
    
    void Foo::init(char x, int y)
    {
      // ...
    }
    
    Run Code Online (Sandbox Code Playgroud)

请参阅C++ FAQ条目以供参考.

  • 实际上非常默认的参数使得_very clean_方式可以做我们通常在C#中调用this()的方法 (75认同)
  • @bobobobo使用默认参数将它们编译到调用者中,因此不是_very_ clean.重载是更多的代码,正确,但实现封装了默认值. (6认同)
  • 请注意,如果要构造的类没有继承或常量字段,则不使用C++ 11的建议解决方案才有效.我没有找到初始化初始化列表之外的父类和常量字段的方法. (5认同)
  • 使用init()的一个缺点是,如果不在构造函数()中初始化它,则不能声明指针或ref为const(如在ref /指针中是const而不是它指向的东西). (3认同)
  • @gen(除了缺少的第二个冒号),它将创建一个临时Foo,然后立即将其丢弃。 (2认同)

Cyr*_* Ka 108

不,你不能在C++ 03中调用另一个构造函数(称为委托构造函数).

这在C++ 11(又名C++ 0x)中有所改变,它增加了对以下语法的支持:(
来自维基百科的示例)

class SomeType
{
  int number;

public:
  SomeType(int newNumber) : number(newNumber) {}
  SomeType() : SomeType(42) {}
};
Run Code Online (Sandbox Code Playgroud)

  • @TomášZato另一个区别是,使用默认参数,您只需要一个构造函数,您必须使用public,protected或private,而使用2个构造函数,一个调用另一个,您可以限制对其中一个的访问,而不必限制访问到另一个. (6认同)
  • 但是这与标准默认参数语法有何不同? (3认同)
  • 它也与默认值不同,因为您可以在不重新编译使用库的代码的情况下进行更改.使用默认值,这些值将"烘焙"到调用中. (2认同)

ohl*_*her 40

我相信你可以从构造函数中调用构造函数.它将编译并运行.我最近看到有人这样做,它在Windows和Linux上运行.

它只是没有做你想要的.内部构造函数将构造一个临时本地对象,一旦外部构造函数返回,该对象就会被删除.它们也必须是不同的构造函数,否则您将创建递归调用.

参考:https://isocpp.org/wiki/faq/ctors#init-methods

  • 这不是"调用构造函数".您可以直接"调用构造函数"的_only_位置在C++ 11的_ctor-initializer_中.你在这个例子中所做的是构建一个对象,这是一个不同的鱼.不要因为_looks_就像对构造函数的函数调用那样被误导,因为它是_not_ one!实际上没有办法对构造函数进行函数调用,这就是为什么不可能构造一个类的实例,该类的唯一构造函数是函数模板的实例化,其模板参数不能推导出来. (9认同)
  • 好点子; 大多数人只是说"不,你不能".我可以 :).我做了这个切换回来,并使用原来的ctor决定调用哪个其他.在调试中,可以在第二个中看到对象,所有内容都被初始化但返回时返回默认值.当你想到它时会有很多意义. (2认同)

kch*_*se2 21

值得指出的是,您可以在构造函数中调用父类的构造函数,例如:

class A { /* ... */ };

class B : public A
{
    B() : A()
    {
        // ...
    }
};
Run Code Online (Sandbox Code Playgroud)

但是,不,你不能调用同一个类的另一个构造函数.

  • 是的。但我在 2008 年 11 月 C++11 发布之前是正确的。 (16认同)

Ben*_*n L 20

C++ 11中,构造函数可以调用另一个构造函数重载:

class Foo  {
     int d;         
public:
    Foo  (int i) : d(i) {}
    Foo  () : Foo(42) {} //New to C++11
};
Run Code Online (Sandbox Code Playgroud)

此外,成员也可以像这样初始化.

class Foo  {
     int d = 5;         
public:
    Foo  (int i) : d(i) {}
};
Run Code Online (Sandbox Code Playgroud)

这应该消除了创建初始化辅助方法的需要.并且仍然建议不要在构造函数或析构函数中调用任何虚函数,以避免使用任何可能未初始化的成员.


lyn*_*gvi 13

如果你想成为邪恶的,你可以使用就地"新"运算符:

class Foo() {
    Foo() { /* default constructor deliciousness */ }
    Foo(Bar myParam) {
      new (this) Foo();
      /* bar your param all night long */
    } 
};
Run Code Online (Sandbox Code Playgroud)

似乎为我工作.

编辑

正如@ElvedinHamzagic指出的那样,如果Foo包含一个分配内存的对象,那么该对象可能不会被释放.这使事情进一步复杂化.

一个更一般的例子:

class Foo() {
private:
  std::vector<int> Stuff;
public:
    Foo()
      : Stuff(42)
    {
      /* default constructor deliciousness */
    }

    Foo(Bar myParam)
    {
      this->~Foo();
      new (this) Foo();
      /* bar your param all night long */
    } 
};
Run Code Online (Sandbox Code Playgroud)

看起来有点不那么优雅,当然.@ JohnIdol的解决方案要好得多.

  • 它似乎不是建议做的事情,因为你可以在10.3结束时阅读http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.3 (4认同)
  • 这几乎可以肯定是UB。 (2认同)
  • 这真是邪恶。假设您正在该构造函数中分配内存,并在析构函数中对其进行分配。没有内存将被释放。 (2认同)
  • 但是,如果您在new(this)Foo();之前显式调用析构函数,则仍然可以从灾难中摆脱出来:`this-&gt;〜Foo();`。 (2认同)

unw*_*ind 8

不,在C++中,您无法从构造函数中调用构造函数.沃伦指出,你能做的是:

  • 使用不同的签名重载构造函数
  • 在参数上使用默认值,以使"更简单"的版本可用

请注意,在第一种情况下,您无法通过从另一个构造函数调用来减少代码重复.您当然可以使用一个单独的private/protected方法来执行所有初始化,并让构造函数主要处理参数处理.


e.J*_*mes 7

另一个尚未显示的选项是将您的类拆分为两个,在您的原始类周围包装一个轻量级接口类,以实现您正在寻找的效果:

class Test_Base {
    public Test_Base() {
        DoSomething();
    }
};

class Test : public Test_Base {
    public Test() : Test_Base() {
    }

    public Test(int count) : Test_Base() {
        DoSomethingWithCount(count);
    }
};
Run Code Online (Sandbox Code Playgroud)

如果您有许多构造函数必须调用它们的“下一级”对应物,这可能会变得混乱,但对于少数构造函数,它应该是可行的。


小智 5

在Visual C++中,您还可以在构造函数中使用此表示法:this-> Classname :: Classname(另一个构造函数的参数).请参阅以下示例:

class Vertex
{
 private:
  int x, y;
 public:
  Vertex(int xCoo, int yCoo): x(xCoo), y(yCoo) {}
  Vertex()
  {
   this->Vertex::Vertex(-1, -1);
  }
};
Run Code Online (Sandbox Code Playgroud)

我不知道它是否在其他地方工作,我只在Visual C++ 2003和2008中测试过它.你也可以用这种方式调用几个构造函数,我想,就像在Java和C#中一样.

PS:坦率地说,我很惊讶这是前面没有提到的.

  • 坦率地说,我对Visual C++允许这一点感到震惊和失望.它非常破碎.我们不要说服人们使用这种策略. (5认同)
  • 这种方法非常危险!如果成员不是来自POD类型,则会产生内存泄漏.例如std :: string. (2认同)

小智 5

简而言之,您不能在C ++ 11之前。

C ++ 11引入了委托构造函数

委托构造函数

如果类本身的名称在成员初始值设定项列表中显示为class-or-identifier,则该列表必须仅由该一个成员初始值设定项组成;这样的构造函数称为委托构造函数,并且由初始化程序列表的唯一成员选择的构造函数是目标构造函数

在这种情况下,将通过重载分辨率选择目标构造函数并首先执行它,然后控件返回到委托构造函数并执行其主体。

委托构造函数不能是递归的。

class Foo {
public: 
  Foo(char x, int y) {}
  Foo(int y) : Foo('a', y) {} // Foo(int) delegates to Foo(char,int)
};
Run Code Online (Sandbox Code Playgroud)

请注意,委派构造函数是一个全有或全无的提议。如果一个构造函数委托给另一个构造函数,则不允许调用构造函数在其初始化列表中包含任何其他成员。如果您考虑一次初始化const / reference成员,并且只初始化一次,则这很有意义。