Can I call a constructor from another constructor

2018-12-31 04:34发布

As a C# developer I'm used to run through constructors:

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

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

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

Is there a way to do this in C++?

I tried calling the Class name and using the 'this' keyword, but both fails.

15条回答
宁负流年不负卿
2楼-- · 2018-12-31 05:05

Would be more easy to test, than decide :) Try this:

#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;
}

and compile it with 98 std: g++ main.cpp -std=c++98 -o test_1

you will see:

A::Ctor
Test constructor delegation a = 9; b = 6
A::dtor

so :)

查看更多
孤独寂梦人
3楼-- · 2018-12-31 05:06

I would propose the use of a private friend method which implements the application logic of the constructor and is the called by the various constructors. Here is an example:

Assume we have a class called StreamArrayReader with some private fields:

private:
    istream * in;
      // More private fields

And we want to define the two constructors:

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

Where the second one simply makes use of the first one (and of course we don't want to duplicate the implementation of the former). Ideally, one would like to do something like:

StreamArrayReader::StreamArrayReader(istream * in_stream){
    // Implementation
}

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

However, this is not allowed in C++. For that reason, we may define a private friend method as follows which implements what the first constructor is supposed to do:

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

Now this method (because it's a friend) has access to the private fields of o. Then, the first constructor becomes:

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

Note that this does not create multiple copies for the newly created copies. The second one becomes:

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

That is, instead of having one constructor calling another, both call a private friend!

查看更多
何处买醉
4楼-- · 2018-12-31 05:09

When calling a constructor it actually allocates memory, either from the stack or from the heap. So calling a constructor in another constructor creates a local copy. So we are modifying another object, not the one we are focusing on.

查看更多
只若初见
5楼-- · 2018-12-31 05:10

I believe you can call a constructor from a constructor. It will compile and run. I recently saw someone do this and it ran on both Windows and Linux.

It just doesn't do what you want. The inner constructor will construct a temporary local object which gets deleted once the outer constructor returns. They would have to be different constructors as well or you would create a recursive call.

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

查看更多
一个人的天荒地老
6楼-- · 2018-12-31 05:13

This approach may work for some kinds of classes (when the assignment operator behaves 'well'):

Foo::Foo()
{
    // do what every Foo is needing
    ...
}

Foo::Foo(char x)
{
    *this = Foo();

    // do the special things for a Foo with char
    ...
}
查看更多
爱死公子算了
7楼-- · 2018-12-31 05:14

If you want to be evil, you can use the in-place "new" operator:

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

Seems to work for me.

edit

As @ElvedinHamzagic points out, if Foo contained an object which allocated memory, that object might not be freed. This complicates things further.

A more general example:

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

Looks a bit less elegant, for sure. @JohnIdol's solution is much better.

查看更多
登录 后发表回答