Страницы

Поиск по вопросам

воскресенье, 8 декабря 2019 г.

Почему не вызывается конструктор копии?

#cpp


Имею такой код

#include 

using 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 - деструктор.

Комментариев нет:

Отправить комментарий