Гарантируется, что деструктор C ++ не будет вызываться до конца блока? - PullRequest
33 голосов
/ 18 января 2010

В приведенном ниже коде C ++ я гарантирую, что деструктор ~ obj () будет вызываться после , когда // Еще код выполняется? Или компилятору разрешено уничтожать объект obj раньше, если он обнаруживает, что он не используется?

{
  SomeObject obj;
  ... // More code
}

Я бы хотел использовать эту технику, чтобы избавить меня от необходимости сбрасывать флаг в конце блока, но мне нужно, чтобы флаг оставался установленным для всего блока.

Ответы [ 8 ]

45 голосов
/ 18 января 2010

Вы в порядке с этим - это очень часто используемый шаблон в программировании на C ++. Из раздела 12.4 / 10 стандарта C ++, в котором говорится о том, когда вызывается деструктор:

для построенного объекта с продолжительность автоматического хранения когда блок, в котором находится объект созданные выходы

24 голосов
/ 18 января 2010

На самом деле ...

C ++ имеет то, что называется принципом «как будто». Все гарантии, указанные во всех этих ответах, относятся только к наблюдаемому поведению. Компилятору разрешается указывать, переупорядочивать, добавлять и т. Д. Любой вызов функции, если наблюдаемое поведение такое же, как если бы оно выполнялось так, как было написано изначально. Это относится и к деструкторам.

Итак, технически ваше наблюдение верно: компилятору разрешено уничтожить объект раньше, если он обнаружит, что он не используется, и нет никаких видимых побочных эффектов от деструктор или любая функция, которую он вызывает. Но вы гарантированно не сможете сказать, что это происходит вне отладчика, потому что, если бы вы могли сказать, компилятор больше не смог бы это сделать.

Скорее всего, компилятор использует эту мощь, чтобы сделать что-то полезное, например, полностью объяснить тривиальный деструктор, а не переупорядочивать вызовы деструктора.

Редактировать: Кто-то хотел ссылку ... 1.9 / 5 вместе со сноской 4 чернового стандарта C ++ 0x (это не новое правило, у меня просто нет Удобный стандарт C ++ 03. Он также присутствует в стандарте C, AFAIK)

1,9 / 5:

Соответствующая реализация, выполняющая правильно сформированную программу, должна производить то же наблюдаемое поведение в качестве одной из возможных последовательностей выполнения соответствующего экземпляра абстрактной машины с та же программа и тот же вход. Однако, если любая такая последовательность выполнения содержит неопределенную операцию, В этом международном стандарте нет требований к выполнению этой программы с этим ввод (даже в отношении операций, предшествующих первой неопределенной операции).

Сноска 4:

Это положение иногда называют правилом «как будто», потому что реализация может свободно игнорировать любое требование этого Международном стандарте до тех пор, пока результат будет соответствовать требованию, насколько это можно определить из наблюдаемое поведение программы. Например, фактическая реализация не должна оценивать часть выражения, если она может сделать вывод, что его значение не используется и что не возникает никаких побочных эффектов, влияющих на наблюдаемое поведение программы.

Мое чтение (и то, что я считал общим пониманием) состояло в том, что это то, что позволяет свободной руке компилятора делать все, что он хочет (т. Е. Позволяет оптимизировать), при условии, что наблюдаемое поведение соответствует исходному письменному источнику - включая движение вокруг деструкторов, отсутствие разрушения объектов, изобретение деструкторов и т. д.

18 голосов
/ 18 января 2010

Деструктор не будет вызываться, пока объект не выйдет из области видимости.

В C ++ faq lite есть хороший раздел о dtors

9 голосов
/ 18 января 2010

Разрушение в C ++ является детерминированным - это означает, что компилятор не может свободно перемещать этот код. (Конечно, оптимизация может встроить деструктор, определить, что код деструктора не взаимодействует с // More code и выполнить переупорядочение команд, но это другая проблема)

Если вы не можете зависеть от вызываемых деструкторов, когда они должны вызываться, вы не можете использовать RAII для захвата блокировок (или просто любой другой конструкции RAII):

{
    LockClass lock(lockData);
    // More code
} // Lock automatically released.

Кроме того, вы можете зависеть от деструкторов, работающих в обратном порядке того, как были построены объекты.

5 голосов
/ 18 января 2010

Да, это гарантировано.

Время жизни объекта с автоматической продолжительностью хранения заканчивается в конце его потенциальной области действия , а не раньше. Для такого объекта потенциальная область видимости начинается в точке объявления и заканчивается в конце блока, в котором он объявлен. Это момент, когда деструктор будет вызван.

Обратите внимание, что очень педантично, даже для автоматического объекта некорректно говорить, что он разрушается, когда он "выходит за рамки видимости" (в отличие от "выходит за пределы своей потенциальной области видимости") ). Объект может выходить из области видимости и возвращаться в область видимости много раз (если в блоке объявляется еще больше локальных объектов с одинаковыми именами), и выход из области видимости таким образом не приводит к разрушению объекта. Это «самый последний конец» своей области действия, который убивает автоматический объект, который определяется как конец его потенциальной области действия , как описано выше.

Фактически, языковой стандарт даже не полагается на понятие scope для описания времени жизни автоматических объектов (нет необходимости разбираться со всеми этими терминологическими сложностями). Это просто говорит о том, что объект уничтожен на выходе из блока, в котором он определен:)

3 голосов
/ 18 января 2010

Да, стандарт C ++ предъявляет особые требования в отношении того, когда объекты уничтожаются (в §12.4 / 10), и в этом случае его нельзя уничтожать до тех пор, пока не закончится выполнение всего другого кода в блоке.

2 голосов
/ 18 января 2010

Все ответы здесь касаются того, что происходит с именованными объектами, но для полноты вы, вероятно, должны знать правило и для временных / анонимных объектов. (например, f(SomeObjectConstructor() или f(someFunctionThatReturnsAnObject()))

Временные объекты уничтожаются как последний шаг в оценке полного выражения (1.9), которое (лексически) содержит точку, где они были созданы. Это верно, даже если эта оценка заканчивается выдачей исключения. (12.2 / 3 из стандарта ISO C ++ 98)

, что в основном означает, что временно созданные объекты сохраняются до следующего оператора. Два исключения относятся к временным файлам, сгенерированным как часть списка инициализации объекта (в этом случае временное уничтожается только после того, как объект полностью построен) и если делается ссылка на временное (например, const Foo& ref = someFunctionThatReturnsAnobject()) (в этом случае время жизни объект - это время жизни ссылки).

1 голос
/ 18 января 2010

Типичным примером этого, как и ваш вопрос, является boost :: scoped_ptr (или аналог std :: auto_ptr):

{
    boost::scoped_ptr< MyClass > pMyClass( new MyClass );

    // code using pMyClass here

} // destruction of MyClass and memory freed
...