Как работает RAII, когда конструктор выдает исключение? - PullRequest
19 голосов
/ 03 февраля 2012

Я изучаю идиому RAII в C ++ и как использовать умные указатели.

В моем чтении я обнаружил две вещи, которые, как мне кажется, противоречат друг другу.

Цитируется из http://www.hackcraft.net/raii/:

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

Но цитируется с http://www.parashift.com/c++-faq-lite/exceptions.html#faq-17.10:

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

И затем второй связанный источник рекомендует использовать умные указатели для решения проблемы вещей, которые уже были выделены в конструкторе.

Так что же на самом деле происходит в этих сценариях?

Ответы [ 3 ]

15 голосов
/ 03 февраля 2012

Вы неправильно поняли первую цитату.Это не сложно, так как это сбивает с толку.

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

Вот что он говорит.Вот что это означало :

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

Видите разницу?Идея состоит в том, что объект-член завершил свой конструктор, а тип-владелец - нет.Он бросил где-то в своем конструкторе (или конструкторе другого члена, который инициализируется после этого).Это приведет к вызову деструктора всех его членов (то есть всех, кто завершил построение), но не его собственного деструктора.

Вот пример:

class SomeType
{
  InnerType val;
public:
  SomeType() : val(...)
  {
    throw Exception;
  }
};

Когда вы создаете экземпляр SomeType, он будет вызывать InnerType::InnerType.Пока это не сработает, он будет входить в конструктор SomeType.Когда это бросает, это приведет к уничтожению val, вызывая, таким образом, InnerType::~InnerType.

5 голосов
/ 03 февраля 2012

Здесь нет противоречия;есть просто некоторая запутанная терминология, используемая в разных контекстах.

Если конструктор объекта выдает исключение, то происходит следующее (при условии, что исключение перехвачено):

  1. Все локальные переменные вконструктор вызывает своих деструкторов, высвобождая все полученные им ресурсы (если таковые имеются).
  2. Все прямые подобъекты объекта, конструктор которого вызвал исключение, будут вызывать свои деструкторы, высвобождая ресурсы, которые они приобрели(если есть).
  3. Все базовые классы объекта, чей конструктор бросил, будут вызывать свои деструкторы (поскольку они были полностью созданы до запуска конструктора производного класса)
  4. Дальнейшая очистка от вызывающей стороны и т. д.будет иметь место.

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

Надеюсь, это поможет!

2 голосов
/ 03 февраля 2012

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

Таким образом, с помощью RAII и интеллектуальных указателей деструкторы для любых членов-указателей объекта будут вызываться независимо от деструктора объекта-должника. Необработанные указатели не освобождают память, на которую они указывают, и должны быть удалены вручную. Если конструктор объекта-владельца бросит необработанные указатели, он не будет освобожден. Это не может произойти с умными указателями.

...