尽管存在非默认构造函数参数

时间:2016-01-16 17:56:18

标签: c++ c++11 constructor initialization object-initializers

以下是一些基本的C ++代码大纲:

#include <cstdlib>
#include <iostream>
#include <thread>

using namespace std;

class M {
public: 
    M() = default;
    ~M() { 
        cout << "Called ~M" << endl; 
    }
};

class A {
public:
    A(int z) : _z(z) {
        cout << "Called A(int z)" << endl;
        this_thread::sleep_for(chrono::milliseconds(1000));
    }

    A() {
        cout << "Called A()" << endl;
        this_thread::sleep_for(chrono::milliseconds(1000));
    }
    A(const A& a) {
        cout << "Called A(const A& a)" << endl;
        _z = a._z;
    }
    A(const A&& a) {
        cout << "Called A(const A&& a)" << endl;
        _z = a._z;
    }
    A& operator=(const A& a) {
        cout << "Called A& operator=(const A& a)" << endl;
        if (&a != this) {
            cout << "Executed A& operator=(const A& a)" << endl;
        }
    }
    virtual ~A() { 
        cout << "Called ~A" << endl; 
    }
    int poll() const { return _z;  }

private:
    M _m;
    int _z = 300;
};

class B : public A {
public:
    // _a(10)
    B() : _a(std::move(A(10))) {
        cout << "Called B()" << endl;
    }
    virtual ~B() { 
        cout << "Called ~B" << endl; 
    }
private:
    const A& _a;
};

int main(int argc, char** argv) {
    B b;    
    A* aPtr = &b;
    A& aRef = (*aPtr);
    cout << aRef.poll() << endl;
    return 0;
}

从上面的设置我得到以下输出:

Called A()
Called A(int z)
Called ~A
Called ~M
Called B()
300
Called ~B
Called ~A
Called ~M

我的问题是输出的第一行(鉴于第一行,所有其他的都有意义)。我正在初始化_a中的成员B() : _a(std::move(A(10))),这是强制的,因为_a是const引用成员。并且带有int参数的CTOR也被调用,但是为什么在A上调用默认的CTOR?为什么不移动CTOR?因此,临时对象似乎只是构造和破坏,没有真正的移动(从后面的300输出中可以看出)。

现在这个问题似乎与移动本身无关,而是与const引用成员周围的行为有关。因为如果我将初始化列表更改为:B(): _a(10)我会遇到同样的问题:不知何故,默认对象被分配给const引用成员,并且忽略初始化列表中的参数。所以对B(): _a(10)我得到:

Called A()
Called A(int z)
Called B()
300
Called ~B
Called ~A
Called ~M

基本上为什么第一行是默认构造函数?我如何更改代码,以便从初始化出现10而不是默认的300?

2 个答案:

答案 0 :(得分:3)

B类的每个对象实际上都有两个类型的子对象。一个是基类子对象,另一个是_a成员子对象。您可以调用该成员的构造函数,但是基类子对象是默认初始化的,因为您还没有在初始化列表中显式调用其构造函数。

你可以通过以下方式来做到这一点:

B() : A(arguments) //<--initialize the base-class subobject
    , _a(std::move(A(10))) {
        cout << "Called B()" << endl;
    }

答案 1 :(得分:2)

B两个包含 A 的实例和派生自A(这可能是一个错误)。< / p>

当您构建临时10对象,然后将其移动到成员 A时,您正在传递_a。您将 base 类子对象保留为默认初始化。

要解决此问题,您需要在成员初始化列表中包含基类:

B() : A(1010), _a(std::move(A(10))) {
    cout << "Called B()" << endl;
}

使用B初始化1010的基类子对象(以区别于成员对象)。

如果我要这样做,我也会直接初始化_a,所以ctor看起来像:

B() : A(1010), _a(10) { // ...