Вот некоторые основные наброски кода на 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
является постоянным справочным членом. Также вызывается CTOR с аргументом int, однако почему CTOR по умолчанию вызывается для A? Почему нет движения CTOR? Поэтому временный объект просто кажется созданным и разрушенным, никакого реального движения не происходит (как можно увидеть из вывода 300 позже).
Теперь эта проблема, похоже, не связана с переездом как таковой но к поведению вокруг константного ссылочного члена. Потому что, если я изменю список инициализации на: 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
член подобъекта. Вы вызываете конструктор для члена, но подобъект базового класса инициализируется по умолчанию, поскольку вы явно не вызывали его конструктор в своем списке инициализации.
Вы можете сделать это, например, следующим образом:
B() : A(arguments) //<--initialize the base-class subobject
, _a(std::move(A(10))) {
cout << "Called B()" << endl;
}
Ваш B
и то и другое содержит экземпляр A
а также происходит от A
(что, вероятно, ошибка).
Вы проходите 10
когда вы строите временный A
объект, затем переместить это в член _a
, Вы покидаете база подобъект класса по умолчанию инициализируется.
Чтобы это исправить, вам нужно включить базовый класс в список инициализатора члена:
B() : A(1010), _a(std::move(A(10))) {
cout << "Called B()" << endl;
}
Это инициализирует подобъект базового класса B
с 1010
(чтобы отличить его от объекта-члена).
Если бы я собирался сделать это, я бы также инициализировал _a
напрямую, поэтому ctor будет выглядеть примерно так:
B() : A(1010), _a(10) { // ...