简体   繁体   中英

Why is this attempting to call the default constructor?

In my original project I have a class whose constructor is supposed to call an initialization function before constructing a class member. However, the compiler is giving me an error for a missing default constructor for the class member.

class One
{
public:
    One(int i) {};
};

class Two
{
    One one;

public:
    Two() {one(0)};
};

int main(void)
{
    Two two;
}

The compiler gives me this:

Problem.cpp: In constructor ‘Two::Two()’:
Problem.cpp:12:15: error: no matching function for call to ‘One::One()’
         Two() {one(0)};
           ^
Problem.cpp:4:9: note: candidate: ‘One::One(int)’
         One(int i) {};
         ^~~
Problem.cpp:4:9: note:   candidate expects 1 argument, 0 provided
Problem.cpp:1:7: note: candidate: ‘constexpr One::One(const One&)’
 class One
       ^~~
Problem.cpp:1:7: note:   candidate expects 1 argument, 0 provided
Problem.cpp:1:7: note: candidate: ‘constexpr One::One(One&&)’
Problem.cpp:1:7: note:   candidate expects 1 argument, 0 provided
Problem.cpp:12:21: error: no match for call to ‘(One) (int)’
         Two() {one(0)};

Why is this code attempting to call a default constructor for One in the Two constructor, and how can I produce the intended behavior (construct member after other initialization code in the constructor)?

how can I produce the intended behavior (construct member after other initialization code in the constructor)?

This is not possible. In C++, all the object's base classes and non-static member variables must be constructed before the body of the constructor is entered.

In your code you wrote the opening { of the Two constructor without having previously offered any constructor arguments for the member variable, so the member variable is default-constructed.


If you have some logic to work out the initializer for one then I would recommend putting that into a function:

int f() { /* logic here */ return 0; }

// ...

Two(): one( f() ) {}

Of course you could also add a default constructor to One plus a means of assigning to it later.

The default constructor is called because you don't call the one that takes an int as a parameter. You have to do that in the initializer list :

class Two
{
    One one;

public:
    Two() : one(0) {}
};

You also need to declare your variable correctly :

Two two;

not

Two two();

By adding an init function and using the comma operator, you can get away with this:

class Two
{
    One one;

    void init()
    {
        // Initialize things here
    }

public:
    Two() : one((init(), 0)) {}
};

First, init() is called, then 0 is passed to the One constructor.

You need this:

class One
{
public:
    One(int i) {};
};

class Two
{
    One one;

public:
    Two() : one(0) {};
};

int main(void)
{
    Two two();
}

The reason it is calling the default constructor for One is that Two::one needs to be constructed before the first line of code in the Two::Two constructor, or else the Two hasn't really been constructed yet. By placing the One constructor call in the initializer list, that ensures that the One gets the arguments that it needs to construct before any non-initializing Two code runs.

The error is because all of Two 's members must be fully constructed before the Two() constructor's body is entered, but you are not explicitly constructing the one member in the Two() constructor's member initialization list, so the compiler attempts to default construct the one member for you, and fails since the One class does not have a default constructor.

To do what you are asking, you need to either:

  • make One be default-constructible, and give it a way to be assigned a new value when needed:

     class One { private: int value; public: One(int i = 0) : value(i) {} One& operator=(int rhs) { value = rhs; return *this; } }; class Two { private: One one; // <-- default constructed! public: Two() { // initialization as needed... one = 0; // <-- reassignment afterwards } }; 
  • change the one member to be a One* pointer instead, and then you can construct it whenever you want, eg:

     class One { private: int value; public: One(int i = 0) : value(i) {} One(const One &src) : value(src.value) {} }; class Two { private: One *one; public: Two() : one(NULL) { // initialization as needed... one = new One(0); } // don't forget about the "Rule of 3"... Two(const Two &src) : one(NULL) { // initialization as needed... one = new One(*(src.one)); } ~Two() { delete one; } Two& operator=(const Two &rhs) { if (&rhs != this) { Two temp(rhs); std::swap(one, temp.one); } return *this; } }; 

    Or, if you are using C++11 or later:

     #include <memory> class Two { private: std::unique_ptr<One> one; public: Two() { // initialization as needed... one.reset(new One(0)); // or: in C++14 and later: // one = std::make_unique<One>(0); } // don't forget about the "Rule of 3"... Two(const Two &src) { // initialization as needed... one.reset(new One(*(src.one))); // or: in C++14 and later: // one = std::make_unique<One>(*(src.one)); } Two& operator=(const Two &rhs) { if (&rhs != this) { Two temp(rhs); std::swap(one, temp.one); } return *this; } // and the "Rule of 5"... Two(Two &&src) { // initialization as needed... one = std::move(src.one); } Two& operator=(Two &&rhs) { one = std::move(rhs.one); return *this; } }; 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM