Резюме
Все запуски этих функций должны быть успешными. Никогда не позволяйте ошибке выйти за пределы деструктора, функции освобождения ресурса (например, оператора delete) или функции обмена. В частности, типы, деструкторы которых могут генерировать исключения, категорически запрещено использовать со стандартной библиотекой С++.
Обсуждение
Перечисленные функции не должны генерировать исключений, так как они являются ключевыми для двух главных операций транзакционного программирования — отката при возникновении проблем в процессе работы и принятия результата работы, если проблем не возникло. Если нет способа безопасного возврата в предыдущее состояние при помощи операций, не генерирующих исключения, то невозможно реализовать бессбойный откат; отсутствие возможности безопасного сохранения изменения состояния при помощи операции, не генерирующей исключения, делает невозможной реализацию бессбойного принятия результата работы.
Рассмотрим следующие советы и требования, найденные в Стандарте С++.
Если деструктор, вызванный в процессе свертки стека, выходит с исключением, вызывается функция terminate
(15.5.1). Поэтому деструкторы должны в общем случае перехватывать исключения и не позволять им распространиться за пределы деструктора.
— [C++03] §15.2(3)
В стандартной библиотеке C++ не определен ни один деструктор [включая деструкторы любого типа, используемого для инстанцирования шаблона стандартной библиотеки] , генерирующий исключение.
— [C++03] §17.4.4.8(3)
Деструкторы являются специальными функциями, и компилятор автоматически вызывает их в различных контекстах. Если вы пишете класс — назовем его, к примеру, Nefarious
[2] Nefarious — нечестивый, гнусный (англ.). — Прим. перев.
— деструктор которого может давать сбой (обычно посредством генерации исключения; см. рекомендацию 72), то вы столкнетесь с такими последствиями.
• Объекты Nefarious
трудно безопасно использовать в обычных функциях. Вы не можете надежно инстанцировать автоматические объекты Nefarious
в области видимости, если возможен выход из этой области видимости посредством исключения. Если это произойдет, деструктор Nefarious
(вызываемый автоматически) может попытаться сгенерировать исключение, которое приведет к неожиданному завершению всей программы посредством вызова Терминатора — std::terminate
(см. также рекомендацию 75).
• Трудно безопасно использовать классы с членами или базовыми классами Nefarious
. Плохое поведение класса Nefarious распространяется на все классы, для которых он является базовым или у которых имеются члены этого типа.
• Вы не можете надежно создавать глобальные либо статические объекты Nefarious
. Исключение, которое может быть сгенерировано их деструкторами, невозможно перехватить.
• Вы не можете надежно создавать массивы объектов Nefarious
. Коротко говоря, массивы при наличии деструкторов, которые могут генерировать исключения, обладают неопределенным поведением, поскольку в этой ситуации просто невозможно придумать способ разумного отката. (Подумайте сами: какой именно код должен сгенерировать компилятор для создания массива из десяти объектов Nefarious
, если у четвертого объекта в конструкторе происходит генерация исключения, а при откате, когда вызываются деструкторы уже сконструированных объектов, один или несколько деструкторов генерируют исключения? Удовлетворительного ответа в этой ситуации просто нет.)
• Вы не можете использовать объекты Nefarious
в стандартных контейнерах. Объекты Nefarious
нельзя хранить в стандартных контейнерах или использовать их с какими-то другими частями стандартной библиотеки. Стандартная библиотека запрещает использование деструкторов, которые могут генерировать исключения.
Функции освобождения ресурсов, включая специальным образом перегруженные операторы operator delete
и operator delete[]
, попадают в ту же категорию, поскольку в общем случае они также используются в процессе "зачистки", в частности, в процессе обработки исключений.
Помимо деструкторов и функций освобождения ресурсов распространенные безопасные методики основаны на том, что операции обмена не генерируют исключений. В данном случае это связано не с использованием их в реализациях отката, а с их использованием в гарантированном принятии результатов работы. Например, вот идиоматическая реализация оператора operator=
для некоторого типа T
, который основан на выполнении копирующего конструктора, за которым следует вызов функции обмена, не генерирующей исключений:
Читать дальше