默认构造函数正在 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
是常量引用成员。带有 int 参数的 CTOR 也会被调用,但是为什么默认的 CTOR 在 A 上调用?为什么不移动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 类型的子对象。一个是基类子对象,另一个是 _a
成员子对象。调用成员的构造函数,但基类子对象是默认初始化的,因为尚未在初始化列表中显式调用其构造函数。
例如,您可以通过以下方式执行此操作:
B() : A(arguments) //<--initialize the base-class subobject
, _a(std::move(A(10))) {
cout << "Called B()" << endl;
}
您的B
既包含A
的实例,又派生自A
(这可能是一个错误)。
当您构造临时A
对象时,您将传递10
,然后将其移动到成员_a
中。您将基类子对象保留为默认初始化状态。
若要解决此问题,需要在成员初始值设定项列表中包含基类:
B() : A(1010), _a(std::move(A(10))) {
cout << "Called B()" << endl;
}
这会使用 1010
初始化 B
的基类子对象(以将其与成员对象区分开来)。
如果我要这样做,我也会直接初始化_a
,所以 ctor 看起来像这样:
B() : A(1010), _a(10) { // ...
相关文章:
- 为什么在没有显式默认构造函数的情况下,将另一个结构封装在联合中作为成员的结构不能编译
- 初始化具有非默认构造函数的std::数组项的更好方法
- 具有默认模板类型的默认构造函数的类型推导
- 如何使用非默认构造函数实例化模板化类
- 有没有一种代码密度较低的方法来使用非默认构造函数初始化数组?
- 声明没有默认构造函数的字段
- 没有默认构造函数作为模板参数的自定义比较器
- C++17 没有默认构造函数的地图放置(私有默认构造函数)
- 使用移动调用对等构造函数unique_ptr默认构造函数
- C++复制构造函数和默认构造函数
- 将向量从 N1 缩小到 N2 项,而不触发默认构造函数并仅使用 move 语义
- 为什么即使我调用参数化构造函数也会调用默认构造函数?
- 具有非默认构造函数的单例类
- 在 C++ 中声明 const 对象需要用户定义的默认构造函数.如果我有一个可变成员变量,为什么不呢?
- 如何处理没有默认构造函数但在另一个构造函数中构造的对象?
- 在C++中使用默认构造函数初始化对象的不同方法
- 在没有默认构造函数的情况下创建的派生对象
- 强制使用默认构造函数对成员进行未初始化的声明
- 使用默认构造函数初始化对象的不同方法
- 创建类类型的动态分配数组,其中类不得具有默认构造函数