#cpp
Имею такой код #includeusing namespace std; class Unit{ int a_; char* pch_; public: Unit(){ cout << "Simple constr" << endl;} Unit(int r) : a_(r), pch_(new char[100]){ cout << "Constr " << endl;} Unit(const Unit& ){ cout << "Constr copy " << endl;} ~Unit(){ delete [] pch_; cout << "Destr" << endl; } }; int main(){ Unit a = 20; return 0; } Вызывается только конструктор с параметром и естественно деструктор. Почему не так: 1. Вызывается конструктор с параметром: Unit(20). 2. Происходит присваивание в объект который не был создан - это копирование. Вызывается конструктор копирования. Как раз rvalue можно передать по const T&. 3. Деструктор для rvalue. 4. Деструктор для a.
Ответы
Ответ 1
В "классическом" С++ (С++98) ваша инициализация копированием (copy-initialization) Unit a = 20; действительно концептуально означала именно Unit a = Unit(20); с применением конструктора конверсии Unit::Unit(int) и затем конструктора копирования Unit::Unit(const Unit &). Однако даже в "классическом" С++ компилятору было разрешено исключать формирование промежуточного временного объекта и оптимизировать код до прямой инициализации (direct-initialization) Unit a(20); т.е. исключать вызов конструктора копирования даже если конструктор копирования обладал побочными эффектами. Эта оптимизация называется copy elision. (Даже при выполнении copy elision наличие доступного конструктора копирования все равно требовалось.) Начиная с С++17 в языке появилась гарантированная copy elision, при котором ваша инициализация гарантированно трактуется как Unit a(20); без требования наличия конструктора копирования. Т.е. в любом случае, ожидать тут обязательного вызова конструктора копирования вы не должны (и не должны были никогда). В "классическом" С++ конструктор копирования тут мог быть вызван, но не более того. В вашем конкретном случае инициализация копированием (copy-initialization) ведет себя идентично прямой инициализации (direct-initialization), но в общем случае существенные различия между этими формами инициализации сохраняются и в С++17. Например struct A { A(int) {} }; struct B { operator int() const { return 0; } }; int main() { B b; A a1(b); // Все в порядке A a2 = b; // Ошибка } P.S. Никакого "присваивания" тут, конечно, нет и в помине.Ответ 2
Пусть наличие = не вводит в вас в заблуждение, форма записи Unit a = 20; называется copy initialization она полностью аналогична записи Unit a(20);, за тем исключением, что не допускает вызова explicit конструктора. Начиная с C++11 вместо них следует использовать list initialization Unit a{20};. Начиная с С++17 добиться вызова конструктора копирования / перемещения при создании временного объекта больше невозможно, т.е. Unit foo(void) { return Unit{Unit{20}}; } Unit a{Unit{foo()}}; Приведет к вызову только одного конструктора.Ответ 3
Потому что Unit a = 20; это по сути то же, что и Unit a(20); просто другая форма записи. Т.е. вызывается конструктор Unit(int r). А при выходе из main - деструктор.
Комментариев нет:
Отправить комментарий