我可以调用一个构造函数从另一个构造函数(做构造函数链)在c++ ?

作为一名c#开发人员,我习惯于遍历构造函数:

class Test {public Test() {DoSomething();}
public Test(int count) : this() {DoSomethingWithCount(count);}
public Test(int count, string name) : this(count) {DoSomethingWithName(name);}}

在c++中有办法做到这一点吗?

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

586091 次浏览

如果我正确理解你的问题,你是在问你是否可以在c++中调用多个构造函数?

如果这就是你想要的,那不可能。

当然,您可以有多个构造函数,每个构造函数都有唯一的参数签名,然后在实例化一个新对象时调用您想要的那个构造函数。

你甚至可以在构造函数的末尾设置默认参数。

但是你不能有多个构造函数,然后分别调用它们。

不,在c++中,你不能从构造函数调用构造函数。正如沃伦指出的,你能做的是:

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

请注意,在第一种情况下,不能通过从一个构造函数调用另一个构造函数来减少代码重复。当然,你可以有一个单独的私有/受保护的方法来完成所有的初始化,让构造函数主要处理参数处理。

是或否,取决于c++的版本。

在c++ 03中,不能从一个构造函数调用另一个构造函数(称为委托构造函数)。

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

class SomeType{int number; 
public:SomeType(int newNumber) : number(newNumber) {}SomeType() : SomeType(42) {}};

c++ 11:是的!

c++ 11及以后也有这个相同的特性(称为委托构造函数)。

语法与c#略有不同:

class Foo {public:Foo(char x, int y) {}Foo(int y) : Foo('a', y) {}};

c++ 03:没有

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

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

    class Foo {public:Foo(char x, int y=0);  // combines two constructors (char) and (char, int)// ...};
  2. Use an init method to share common code:

    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){// ...}

See the C++FAQ entry for reference.

# 0:是的!

c++ 11及以后也有这个相同的特性(称为委托构造函数)。

语法与c#略有不同:

class Foo {public:Foo(char x, int y) {}Foo(int y) : Foo('a', y) {}};

# 0:不

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

 class A { /* ... */ };    
class B : public A{B() : A(){// ...}};

但是,在c++ 03之前,你不能调用同一个类的另一个构造函数。

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

它不是你想要的。内部构造函数将构造一个临时的局部对象,该对象在外部构造函数返回时被删除。它们也必须是不同的构造函数,否则你将创建一个递归调用。

裁判:# 0

如果你想变邪恶,你可以使用“new”操作符:

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

似乎对我有用。

编辑

正如@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 */}};

看起来确实没那么优雅。@JohnIdol的解决方案好多了。

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

class Foo  {int d;public:Foo  (int i) : d(i) {}Foo  () : Foo(42) {} //New to C++11};

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

class Foo  {int d = 5;public:Foo  (int i) : d(i) {}};

这样就不需要创建初始化帮助器方法了。并且仍然建议不要在构造函数或析构函数中调用任何虚函数,以避免使用任何可能未初始化的成员。

另一个还没有展示的选项是将你的类分成两个,在你的原始类周围包装一个轻量级的接口类,以达到你想要的效果:

class Test_Base {public Test_Base() {DoSomething();}};
class Test : public Test_Base {public Test() : Test_Base() {}
public Test(int count) : Test_Base() {DoSomethingWithCount(count);}};

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

在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);}};

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

附言:坦率地说,我很惊讶之前没有提到这一点。

当调用构造函数时,它实际上是从堆栈或堆中分配内存。因此,在另一个构造函数中调用一个构造函数会创建一个本地副本。所以我们正在修改另一个对象,而不是我们所关注的对象。

我建议使用private friend方法,该方法实现构造函数的应用程序逻辑,并由各种构造函数调用。这里有一个例子:

假设我们有一个名为StreamArrayReader的类,它有一些私有字段:

private:istream * in;// More private fields

我们想定义两个构造函数:

public:StreamArrayReader(istream * in_stream);StreamArrayReader(char * filepath);// More constructors...

其中第二个简单地使用了第一个(当然,我们不想复制前者的实现)。理想情况下,人们会做如下事情:

StreamArrayReader::StreamArrayReader(istream * in_stream){// Implementation}
StreamArrayReader::StreamArrayReader(char * filepath) {ifstream instream;instream.open(filepath);StreamArrayReader(&instream);instream.close();}

然而,这在c++中是不允许的。因此,我们可以定义一个私有友方法,如下所示,它实现了第一个构造函数应该做的事情:

private:friend void init_stream_array_reader(StreamArrayReader *o, istream * is);

现在这个方法(因为它是一个朋友)可以访问o的私有字段。然后,第一个构造函数变成:

StreamArrayReader::StreamArrayReader(istream * is) {init_stream_array_reader(this, is);}

注意,这不会为新创建的副本创建多个副本。第二个是:

StreamArrayReader::StreamArrayReader(char * filepath) {ifstream instream;instream.open(filepath);init_stream_array_reader(this, &instream);instream.close();}

那就是不是一个构造函数调用另一个构造函数,而是两个构造函数都调用一个私有友元!

这种方法可能适用于某些类型的类(当赋值操作符表现“良好”时):

Foo::Foo(){// do what every Foo is needing...}
Foo::Foo(char x){*this = Foo();
// do the special things for a Foo with char...}

测试比决定更容易:)试试这个:< / p >

#include <iostream>
class A {public:A( int a) : m_a(a) {std::cout << "A::Ctor" << std::endl;}~A() {std::cout << "A::dtor" << std::endl;}public:int m_a;};
class B : public A {public:B( int a, int b) : m_b(b), A(a) {}public:int m_b;};
int main() {B b(9, 6);std::cout << "Test constructor delegation a = " << b.m_a << "; b = " << b.m_b << std::endl;return 0;}

和98 std编译它:g++ main.cpp -std=c++98 -o test_1

你会看到:

A::CtorTest constructor delegation a = 9; b = 6A::dtor

所以:)

简单地说,你不能在c++ 11之前。

c++ 11引入委托构造函数:

委托构造函数

类的名称本身作为类或标识符出现在成员初始化列表,则列表必须由该成员组成初始化;这样的构造函数称为委托类的唯一成员所选择的构造函数初始化列表是目标构造函数

在这种情况下,目标构造函数是通过重载选择的解析并首先执行,然后控件返回到

.委托构造函数及其主体被执行

委托构造函数不能递归。

class Foo {public:Foo(char x, int y) {}Foo(int y) : Foo('a', y) {} // Foo(int) delegates to Foo(char,int)};

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