Так что мой вопрос таков: если бросок из деструктора приводит к
неопределенное поведение, как вы обрабатываете ошибки, которые происходят во время
деструктор?
Основная проблема заключается в следующем: вы не можете потерпеть неудачу . Что значит потерпеть неудачу, в конце концов? Если фиксация транзакции в базе данных не удалась и не удалась (не удалось выполнить откат), что произойдет с целостностью наших данных?
Поскольку деструкторы вызываются как для нормального, так и для исключительного (сбойного) пути, они сами не могут потерпеть неудачу, иначе мы "не сможем потерпеть неудачу".
Это концептуально сложная проблема, но часто решение состоит в том, чтобы просто найти способ убедиться, что сбой не может закончиться неудачей. Например, база данных может записать изменения до фиксации во внешней структуре данных или файле. Если транзакция не удалась, тогда структура файла / данных может быть отброшена. Все, что от него требуется, - убедиться, что фиксация изменений из этой внешней структуры / файла является атомарной транзакцией, которая не может завершиться неудачей.
Прагматичное решение, возможно, просто убедиться, что шансы
потерпеть неудачу на астрономически невероятно, так как создание вещей
невозможно потерпеть неудачу потерпеть неудачу может быть почти невозможно в некоторых случаях.
Самое правильное решение для меня - написать логику неочищения таким образом, чтобы логика очистки не могла потерпеть неудачу. Например, если у вас возникнет желание создать новую структуру данных, чтобы очистить существующую структуру данных, возможно, вы захотите заранее создать эту вспомогательную структуру, чтобы нам больше не приходилось создавать ее внутри деструктора.
Это все гораздо легче сказать, чем сделать, по общему признанию, но это единственный действительно правильный способ, которым я вижу, чтобы сделать это. Иногда я думаю, что должна быть возможность написать отдельную логику деструктора для нормальных путей выполнения, отличных от исключительных, поскольку иногда деструкторы чувствуют, что у них двойная ответственность, пытаясь справиться с обоими (например, защитники области действия, которые требуют явного увольнения они не потребовали бы этого, если бы могли отличить исключительные пути разрушения от неисключительных).
Тем не менее, главная проблема заключается в том, что мы не можем потерпеть неудачу, и это сложная концептуальная проектная проблема, которую нужно идеально решить во всех случаях. Это становится легче, если вы не слишком запутаетесь в сложных управляющих структурах с тоннами маленьких объектов, взаимодействующих друг с другом, и вместо этого смоделируете свои проекты немного громоздким способом (например: система частиц с деструктором, чтобы уничтожить всю частицу система, а не отдельный нетривиальный деструктор на частицу). Когда вы моделируете свои проекты на таком более грубом уровне, у вас есть меньше нетривиальных деструкторов, с которыми вы можете иметь дело, и вы также можете часто позволить себе любую дополнительную нагрузку на память / обработку, чтобы ваши деструкторы не могли выйти из строя.
И это одно из самых простых решений, естественно - реже использовать деструкторы. В приведенном выше примере с частицами, возможно, при уничтожении / удалении частицы, должны быть сделаны некоторые вещи, которые могут потерпеть неудачу по любой причине. В этом случае, вместо того, чтобы вызывать такую логику через dtor частицы, которая может быть выполнена по исключительному пути, вы могли бы вместо этого сделать все это системой частиц, когда удаляет частицу. Удаление частицы всегда может быть сделано во время неисключительного пути. Если система разрушена, возможно, она может просто очистить все частицы и не беспокоиться об этой логике удаления отдельных частиц, которая может дать сбой, тогда как логика, которая может дать сбой, выполняется только во время нормального выполнения системы частиц, когда она удаляет одну или несколько частиц.
Часто бывают решения, подобные тем, которые возникают, если вы избегаете иметь дело с множеством маленьких объектов с нетривиальными деструкторами. Где вы можете запутаться в беспорядке, где кажется почти невозможным быть безопасным от исключений, когда вы запутываетесь в множестве маленьких объектов, у которых все нетривиальные dtors.
Было бы очень полезно, если бы nothrow / noexcept фактически перевелся в ошибку компилятора, если бы что-либо, что его указывает (включая виртуальные функции, которые должны наследовать спецификацию noexcept своего базового класса), попыталось вызвать что-нибудь, что может выдать. Таким образом, мы сможем поймать все эти вещи во время компиляции, если на самом деле напишем деструктор, который может случайно сгенерировать.