尽管有非默认构造函数参数,但在 const 引用成员上调用默认构造函数
Default constructor is getting called on a const reference member despite non default constructor arguments
下面是一些基本的 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
我的问题是输出的第一行(考虑到第一行,所有其他问题都有意义)。我在 B() : _a(std::move(A(10)))
中初始化成员 _a
,这是强制的,因为 _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?
每个B类型的对象实际上有两个个A类型的子对象,一个是base-class子对象,一个是_a
成员子对象.您为成员调用了构造函数,但是 base-class 子对象是 default-initialized,因为您没有在初始化列表中显式调用它的构造函数。
您可以通过以下方式完成,例如:
B() : A(arguments) //<--initialize the base-class subobject
, _a(std::move(A(10))) {
cout << "Called B()" << endl;
}
您的 B
都 包含 A
和 的实例源自 A
(这可能是一个错误)。
当您构建一个临时 A
对象时,您正在传递 10
,然后将其移动到 成员 _a
。您要让 base class 子对象默认初始化。
要解决这个问题,您需要在成员初始值设定项列表中包含基础 class:
B() : A(1010), _a(std::move(A(10))) {
cout << "Called B()" << endl;
}
这将 B
的基础 class 子对象初始化为 1010
(以区别于成员对象)。
如果我要这样做,我也会直接初始化 _a
,所以 ctor 看起来像:
B() : A(1010), _a(10) { // ...
下面是一些基本的 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
我的问题是输出的第一行(考虑到第一行,所有其他问题都有意义)。我在 B() : _a(std::move(A(10)))
中初始化成员 _a
,这是强制的,因为 _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?
每个B类型的对象实际上有两个个A类型的子对象,一个是base-class子对象,一个是_a
成员子对象.您为成员调用了构造函数,但是 base-class 子对象是 default-initialized,因为您没有在初始化列表中显式调用它的构造函数。
您可以通过以下方式完成,例如:
B() : A(arguments) //<--initialize the base-class subobject
, _a(std::move(A(10))) {
cout << "Called B()" << endl;
}
您的 B
都 包含 A
和 的实例源自 A
(这可能是一个错误)。
当您构建一个临时 A
对象时,您正在传递 10
,然后将其移动到 成员 _a
。您要让 base class 子对象默认初始化。
要解决这个问题,您需要在成员初始值设定项列表中包含基础 class:
B() : A(1010), _a(std::move(A(10))) {
cout << "Called B()" << endl;
}
这将 B
的基础 class 子对象初始化为 1010
(以区别于成员对象)。
如果我要这样做,我也会直接初始化 _a
,所以 ctor 看起来像:
B() : A(1010), _a(10) { // ...