Страницы

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

среда, 10 октября 2018 г.

Порядок уничтожения временных объектов

Недавно столкнулся с некоторой странностью при уничтожении временных объектов. Собственно, вопрос следующий: почему объект под номером 2 удаляется раньше объекта под номером 1? Разве не должны выполняться деструкторы аргументов при выходе из области видимости, то есть при возврате из функции? Почему вначале удаляется временный объект из функции main, а затем аргумент foo? Почему не наоборот?
Код:
#include using std::clog;
struct A { static size_t counter; size_t me = counter++; A() { clog << "constructor A: " << me << "
"; } A(const A& o) { clog << "copy A: " << me << "
"; } A(A&& o) { clog << "move A: " << me << "
"; } ~A() { clog << "destructor A: " << me << "
"; } }; size_t A::counter = 0;
A foo(A obj) { clog << "into foo
"; return obj; }
int main() { A a; clog << "before foo
"; foo(a); clog << "between foo
"; A other = foo(a); clog << "after foo
"; }
Вывод:
constructor A: 0 before foo copy A: 1 into foo move A: 2 destructor A: 2 destructor A: 1 between foo copy A: 3 into foo move A: 4 destructor A: 3 after foo destructor A: 4 destructor A: 0
Компилятор GCC 5.1.1


Ответ

Деструкторы вызываются в порядке обратном относительно вызывов конструкторов пл принципу стека LIFO (Last Input First Output). Сначало был создан объект с номером 1 посредством вызова конструктора копирования, так как исходный объект это lvalue . В стек также был занесен его деструктор. Затем внутри функции был вызван конструктор перемешения с номером 2 благодаря RVO (return value optimization). Этот конструктор вызван после конструктора параметра, а, следовательно, его деструктор помещен в стек перед деструктором объекта с номером 1.
Таким образом последним помещенным в стек, объект с номером 2 удаляется первым.
Согласно стандарту C++ (12.2 Temporary objects)
5....The destruction of a temporary whose lifetime is not extended by being bound to a reference is sequenced before the destruction of every temporary which is constructed earlier in the same full-expression
Переводя на русский язык, эта цитата из стандарта говорит о следующем: "Уничтожение временного объекта, чье время жизни не продлевается за счет привязки к ссылке, происходит перед уничтожением каждого временного объекта, который был создан ранее в том же самом полном выражении"
Что касается второго предложения
A other = foo(a);
то временный объект строится непосредственно в объекте other, а потому и удаляется, когда сам этот объект удаляется.
Если вы посмотрите на сообщения, которые сосответствуют этому предложению
move A: 4 destructor A: 3 after foo destructor A: 4
то вы увидите, что благодаря RVO (return value optimization) локальный объект функции (ее параметр), который является lvalue, сразу же перемещается в объект other. На это указывает сообзения
after foo destructor A: 4
То есть этот временный объект был построен в other и, соответственно удален после сообщения
after foo
когда функция main завершила свою работу.

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

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