为什么要尝试调用默认构造函数?

时间:2018-07-09 22:34:15

标签: c++ gcc constructor

在我的原始项目中,我有一个类,该类的构造函数应该在构造类成员之前调用初始化函数。但是,编译器给我一个错误,提示缺少类成员的默认构造函数。

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

class Two
{
    One one;

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

int main(void)
{
    Two two;
}

编译器给我这个:

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

为什么这段代码试图为Two构造函数中的One调用默认构造函数,以及如何产生预期的行为(在构造函数中其他初始化代码之后的构造成员)?

4 个答案:

答案 0 :(得分:4)

  

如何产生预期的行为(在构造函数中其他初始化代码之后构造成员)?

这是不可能的。在C ++中,必须在输入构造函数的主体之前构造对象的所有基类和非静态成员变量。

在您的代码中,您编写了{构造函数的开头Two,而之前没有为该成员变量提供任何构造函数参数,因此该成员变量是默认构造的。


如果您有一些逻辑可以计算出one的初始值设定项,那么我建议您将其放入函数中:

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

// ...

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

当然,您还可以在One中添加默认构造函数,以及以后分配给它的方法。

答案 1 :(得分:2)

之所以调用默认构造函数,是因为您没有调用以int作为参数的构造函数。您必须在初始化列表中进行此操作:

class Two
{
    One one;

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

您还需要正确声明变量:

Two two;

不是

Two two();

通过添加一个init函数并使用逗号运算符,您可以摆脱这种情况:

class Two
{
    One one;

    void init()
    {
        // Initialize things here
    }

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

首先,调用init(),然后将0传递给One构造函数。

答案 2 :(得分:1)

您需要这个:

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

class Two
{
    One one;

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

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

调用One的默认构造函数的原因是,Two::one必须在Two::Two构造函数的第一行代码之前构造,否则需要{{1} }尚未真正构建。通过将Two构造函数调用放在初始化列表中,可以确保One在运行任何未初始化的One代码之前获取需要构造的参数。

答案 3 :(得分:0)

该错误是因为在输入Two构造函数的 body 之前,必须先完全构造所有Two()成员,但是您是没有在one构造函数的成员初始化列表中显式构造Two()成员,因此编译器尝试为您默认构造one成员,但由于One类没有默认的构造函数。

要执行您的要求,您需要:

  • 使One是默认可构造的,并为它提供了一种在需要时为其分配新值的方法:

    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
        }
    };
    
  • 改为将one成员更改为One*指针,然后您可以随时构造它,例如:

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

    或者,如果您使用的是C ++ 11或更高版本:

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