#cpp
Почему при вызове func2() деструктор для "a" вызывается при завершении функции, а при вызове func() при при завершении main? class A{ public: ~A(){ std::cout << "~" << std::endl; std::cout << *n << std::endl; std::cout << "END of " << n << std::endl; delete n; } int* n; }; A func(){ A a; a.n = new int(5); return a; }; int* func2(){ A a; a.n = new int(3); return a.n; } int main() { auto funcRes = func(); auto func2Res = func2(); std::cout << "func().n = " << *funcRes.n << std::endl; std::cout << "func2() = " << *func2Res << std::endl; } Вывод на консоль: ~ 3 END of 0xf4fc40 func().n = 5 func2() = 0 ~ 5 END of 0xf4fc20
Ответы
Ответ 1
Давайте для начала разберём вариант с func2(). Внутри func2() создается локальный объект типа A, который как и любой локальный объект живёт только внутри блока (области видимости), в котором он определён. После выхода из этой области видимости будет вызван деструктор и объект будет уничтожен. В частности мы внутри деструктора освободим память, которая выделилась под целочисленную переменную, на которую указывает член n. Таким образом внутри main при разименовывании *func2Res мы будем обращаться к памяти, которая нам уже не принадлежит. Что же происходит при вызове функции func? На самом деле вместо того, чтобы создавать новый объект внутри main с помощью конструктора копирования будет использован тот объект, который был создан внутри функции func. Эта оптимизация называется NRVO (Named return value optimization). Немножко подробнее: RVO(return value optimization) позволяет компилятору оптимизировать следующие конструкции: class A{}; A func() { return A(); // будет вызван конструктор } int main() { A a = func(); // конструктор вызван не будет, так как сработала оптимизация } NRVO расширяет возможности оптимизации и позволяет компилятору оптимизировать не только возвращение объектов созданных непосредственно при вызове return, но и разных именованных значений, которые былии объявлены в этой функции раньше: class A{}; A func() { A a; // конструктор будет вызван только тут return a; } int main() { A a = func() // конструктор копирования не будет вызван. сработает NRVO } Что касается стандарта, то он неь гарантирует, что компилятор должен оптимизировать подобные выражения, но разрешает иметь им такое поведение. Смотреть 12.8.* Подробнее: 1. Named Return Value Optimization in Visual C++ 2. RVO и NRVOОтвет 2
Обратите внимание, что в вопросе вы все перепутали. func() инициализируется пятеркой, а на печать деструктор сначала выводит тройку. Значит, когда вызывается func() как раз деструктор не вызывается - он вызывается в конце main. Это потому, что работает RVO (оптимизация возвращаемого значения) - при возврате экземпляра объекта не происходит его пересоздания, при вызове func, A создается сразу внутри funcRes А в случае с func2(), где как раз фигурирует тройка, a живет только на время существования тела функции func2(); Отсюда порядок работы А(5) создалось сразу в funcRes Создалось A(3) Уничтожилось A(3) Уничтожилось funcRes(5)
Комментариев нет:
Отправить комментарий