Это напоминает мне вопрос от коллеги, когда я объяснил ему концепцию исключения / RAII: «Эй, какое исключение я могу выбросить, если компьютер выключен?»
В любом случае, я согласен с ответом Мартина Йорка RAII против исключений
Как обстоят дела с исключениями и деструкторами?
Многие функции C ++ зависят от деструкторов, не производящих метание.
Фактически, вся концепция RAII и ее взаимодействие с ветвлением кода (возвраты, выбросы и т. Д.) Основаны на том факте, что освобождение не приведет к ошибке. Точно так же некоторые функции не должны завершаться сбоем (например, std :: swap), когда вы хотите предложить своим объектам гарантии высокого исключения.
Не то чтобы это не означало, что вы не можете создавать исключения через деструкторы. Просто язык даже не пытается поддерживать это поведение.
Что произойдет, если это будет разрешено?
Просто ради забавы, я пытался это представить ...
Если ваш деструктор не освободит ваш ресурс, что вы будете делать? Ваш объект, вероятно, наполовину разрушен, что бы вы сделали из "внешнего" улова с этой информацией? Попробуйте снова? (если да, то почему бы не попытаться снова изнутри деструктора? ...)
То есть, если бы вы могли в любом случае получить доступ к своему полуразрушенному объекту: что, если ваш объект находится в стеке (что является основным способом работы RAII)? Как вы можете получить доступ к объекту за его пределами?
Отправка ресурса внутри исключения?
Ваша единственная надежда состоит в том, чтобы отправить «дескриптор» ресурса внутри исключения и код надежды в улове, ну ... попробуйте еще раз, чтобы освободить его (см. Выше)?
А теперь представьте что-нибудь смешное:
void doSomething()
{
try
{
MyResource A, B, C, D, E ;
// do something with A, B, C, D and E
// Now we quit the scope...
// destruction of E, then D, then C, then B and then A
}
catch(const MyResourceException & e)
{
// Do something with the exception...
}
}
Теперь давайте представим, что по какой-то причине деструктор D не может освободить ресурс. Вы закодировали его, чтобы отправить исключение, которое будет поймано уловом. Все идет хорошо: вы можете справиться с неудачей так, как вы хотите (как вы конструктивно по-прежнему ускользаете от меня, но тогда это не проблема сейчас).
Но ...
Отправка ресурсов MULTIPLE внутри исключений MULTIPLE?
Теперь, если ~ D может потерпеть неудачу, то ~ C тоже может. а также ~ B и ~ A.
В этом простом примере у вас есть 4 деструктора, которые потерпели неудачу в тот же момент (выход из области видимости). Вам нужен не улов с одним исключением, а улов с массивом исключений (будем надеяться, что сгенерированный для этого код не ... э ... бросает).
catch(const std::vector<MyResourceException> & e)
{
// Do something with the vector of exceptions...
// Let's hope if was not caused by an out-of-memory problem
}
Давайте вернемся ( Мне нравится эта музыка ... ): Каждое выбрасываемое исключение отличается (, потому что причина другая: Помните, что в C ++ исключения не должны происходить из std :: исключение ). Теперь вам нужно одновременно обработать четыре исключения. Как вы могли бы написать предложения catch, обрабатывающие четыре исключения по их типам и по порядку их выдачи?
А что, если у вас есть несколько исключений одного и того же типа, которые вызываются множественным неудачным освобождением? А что, если при выделении памяти массивов исключений из массивов вашей программе не хватает памяти и, э-э ..., выдается исключение из нехватки памяти?
Вы уверены, что хотите потратить время на решение такого рода проблем, вместо того, чтобы тратить их на выяснение причин неудачного освобождения или того, как реагировать на него по-другому?
Очевидно, что разработчики C ++ не нашли жизнеспособного решения, а просто сократили свои потери там.
Проблема не в RAII, а в исключениях ...
Нет, проблема в том, что иногда вещи могут потерпеть неудачу настолько, что ничего не поделаешь.
RAII хорошо работает с исключениями, если выполняются некоторые условия. Среди них: Деструкторы не будут выбрасывать . То, что вы видите как оппозицию, - это всего лишь угловой случай единого шаблона, объединяющего два «имени»: исключение и RAII
В случае возникновения проблемы в деструкторе, мы должны принять поражение и спасти то, что можно спасти : «Не удалось освободить соединение с БД? Извините. По крайней мере, давайте избежим этой утечки памяти и закройте этот файл. "
Хотя шаблон исключения является (предполагается) основной обработкой ошибок в C ++, он не единственный. Вы должны обрабатывать исключительные (каламбур) случаи, когда исключения C ++ не являются решением, используя другие механизмы ошибок / журналов.
Поскольку вы только что встретили стену на этом языке, стена, которую ни один другой язык, о котором я знаю или слышал, не прошел правильно, не обрушив дом (попытка C # была достойной, в то время как одна из Java все еще шутка, которая причиняет мне боль на стороне ... я даже не буду говорить о языках сценариев, которые будут молчать и терпеть неудачу в той же проблеме).
Но, в конце концов, независимо от того, сколько кода вы напишите, пользователь не будет защищен выключением компьютера .
Лучшее, что вы можете сделать, вы уже написали это. Мои собственные предпочтения связаны с методом метания finalize, ресурсами очистки деструктора non-throwing, которые не были завершены вручную, и журналом / окном сообщений (если возможно) для предупреждения о сбое в деструкторе.
Возможно, вы не устраиваете правильный поединок. Вместо «RAII против исключения», это должно быть « Попытка освободить ресурсы против ресурсов, которые абсолютно не хотят быть освобожденными, даже когда им угрожает разрушение »
: -)