Если нельзя бросать исключения, симулируйте RAII для управления ресурсами
Причина
Даже без исключений RAII обычно является лучшим и наиболее систематическим способом работы с ресурсами.
Примечание
Обработка ошибок с помощью исключений — единственный полный и систематический способ обработки нелокальных ошибок в C++. В частности, для неинтрузивной сигнализации о неудаче конструирования объекта требуется исключение. Сигнализация об ошибках способом, который нельзя игнорировать, требует исключений. Если вы не можете использовать исключения, имитируйте их использование наилучшим образом.
Значительная часть страха перед исключениями беспочвенна. При использовании в исключительных обстоятельствах в коде, не засорённом указателями и сложными управляющими структурами, обработка исключений почти всегда доступна (по времени и памяти) и почти всегда приводит к лучшему коду. Это, конечно, предполагает хорошую реализацию механизмов обработки исключений, которая доступна не на всех системах. Также существуют случаи, когда вышеупомянутые проблемы неприменимы, но исключения не могут использоваться по другим причинам. Некоторые системы жёсткого реального времени являются примером: операция должна завершиться в фиксированное время с ошибкой или правильным ответом. В отсутствие подходящих инструментов оценки времени это трудно гарантировать для исключений. Такие системы (например, программное обеспечение управления полётом) обычно также запрещают использование динамической (кучной) памяти.
Итак, основное руководство по обработке ошибок — «использовать исключения и RAII». Этот раздел посвящён случаям, когда у вас нет эффективной реализации исключений или когда у вас такое нагромождение старомодного кода (например, множество указателей, нечётко определённое владение и много несистематической обработки ошибок на основе проверки кодов ошибок), что введение простой и систематической обработки исключений нецелесообразно.
Прежде чем осуждать исключения или слишком много жаловаться на их стоимость, рассмотрите примеры использования кодов ошибок. Рассмотрите стоимость и сложность использования кодов ошибок. Если вас беспокоит производительность, измерьте её.
Пример
Предположим, вы хотели написать:
void func(zstring arg)
{
Gadget g {arg};
// ...
}
Если gadget не был правильно сконструирован, func выходит с исключением. Если мы не можем бросить исключение, мы можем имитировать этот RAII-стиль управления ресурсами, добавив функцию-член valid() к Gadget:
error_indicator func(zstring arg)
{
Gadget g {arg};
if (!g.valid()) return gadget_construction_error;
// ...
return 0; // ноль означает «хорошо»
}
Проблема, конечно, в том, что теперь вызывающая сторона должна помнить о проверке возвращаемого значения. Чтобы поощрить это, рассмотрите добавление [[nodiscard]].
Смотрите также: Обсуждение
Контроль
Возможно (только) для конкретных версий этой идеи: например, проверка систематической проверки valid() после конструирования дескриптора ресурса