Итак, в основном у меня есть этот простой код-обертка для внешней библиотеки C, и я новичок с точки зрения правильной обработки исключений.
Заранее: код показывает одну и ту же проблему два раза, но, возможно, есть иное решение для версии класса.
#include <some_c_lib>
void setup(){
//some setup code
//init function from the C library
//with C-style return code for error handling
if(!init()){
//error: program should terminate
//because error cannot be handled
}
//some setup code
}
class myclass{
//some members
public:
myclass(){
//some construction code
//create function of the C library
//with C-style return code error handling
if(!create()){
//error: program should terminate
//because error cannot be handled
}
}
~myclass(){
//desturction code
}
};
int main(){
std::ostream log("log.txt"); //logfile as an example
setup();
myclass obj;
while(everything_is_fine()){
//main loop stuff
}
}
Проблема в том, что я не знаю, как лучше всего завершить программу.
Я не хочу ловить исключение в main
, Это было бы бессмысленно и уродливо, потому что исключение не может быть обработано в любом случае. Несмотря на это, я хотел бы иметь какой-то механизм разматывания стека. Если бы я просто exit
программа внутри if
блоки, то файл журнала, например, не будет уничтожен должным образом. Я прав?
Будет ли файл закрыт, если я брошу внутрь if
но без предоставления блока try-catch где-нибудь?
Как бороться с исключениями, возникающими в конструкторах?
Есть ли лучший способ справиться с этим типом проблемы?
Надеюсь, стало ясно, в чем моя проблема.
Спасибо за ваши ответы, хорошего дня или вечера.
Это зависит, и вы действительно не дали достаточно информации. Вообще говоря, не существует абсолютного «лучшего» — оно зависит от потребностей вашей программы, а не от подхода «один размер подходит всем».
Это верно только для небольших тривиальных программ (например, такого рода действия, которые вы будете выполнять на занятиях, а не на рабочем месте), что ошибка всегда требует немедленного завершения программы. Реальная потребность более размыта, чем это — в зависимости от того, что делает ваша программа, часто есть вариант восстановления после ошибки и продолжения (либо в обычном, либо в каком-то ухудшенном режиме). Также может быть предпочтительным предпринять шаги для предотвращения ошибки (например, обнаружить неверные данные и сделать что-то для восстановления перед выполнением операции с неверными данными и возникновением состояния ошибки).
Однако, как правило, если в конструкторе возникает ошибка (и она неизбежна, и конструктор не может ничего сделать для восстановления после ее возникновения и т. Д.), Необходимо вызвать исключение. Это в основном сигнализирует о необходимости того, чтобы вызывающая сторона (или некоторая функция в стеке вызовов) предприняла действие восстановления. Если вызывающая сторона не может восстановиться, результатом исключения по умолчанию является завершение программы — после вызова деструкторов всех объектов, созданных локально (с длительностью автоматического хранения) в стеке вызовов. Это завершает программу — если это необходимо — и выполняет очистку в процессе, пока деструкторы очищаются должным образом (что является целью деструкторов).
В вашем коде создание исключения (в конце концов) вернет управление main()
, Если исключение не перехвачено, программа завершит работу, но не раньше log
уничтожен — что вызывает его деструктор. Деструктор стандартных выходных потоковых классов обычно очищает поток и правильно его закрывает. Если вам нужно сделать больше, чем это (например, другое действие восстановления перед завершением, после очистки потока), напишите main()
как функциональный блок.
Обычно нежелательно выполнять «частичную конструкцию» в конструкторе — например, конструктор, устанавливающий некоторые основы, но затем пользователю приходится вызывать другую функцию для «дальнейшей» инициализации. Такие методы дают возможность забыть выполнить инициализацию — это в основном означает, что последующий код получает возможность использовать объект, который не инициализирован должным образом. В C ++ такие приемы редко бывают необходимыми в любом случае — можно задержать создание объекта, пока не будет доступна вся информация для его правильной инициализации (в конструкторе).
Вообще говоря, возврат кодов ошибок (например, функция сvoid
возвращаемый тип, функция, которая принимает указатель / ссылку на объект, который хранит информацию о состоянии), подходит в различных обстоятельствах. Нет ничего, что заставляет вызывающую сторону проверять возвращаемое значение из функции. Поэтому код возврата подходит, если условие ошибки можно безопасно игнорировать (например, если ваш код забыл его проверить) или если функция используется только в тех случаях, когда код возврата будет проверен. Ничто не мешает вам писать код, который преобразует код возврата (скажем, из функции, написанной на C) в исключение. Проблема с кодами возврата заключается в том, что можно забыть проверить их — это может означать, что критические ошибки остаются необнаруженными / незарегистрированными и вызывают ошибки в другом коде в программе.
Одно из решений — не писать ваши конструкторы таким образом, чтобы они генерировали ошибки. Обычной практикой является использование конструкторов для установки переменных-членов и т. Д., А затем использование метода, такого как bool initialize (); это возвращает значение, основанное на том, может ли класс выполнить более сложную инициализацию без ошибки.
Вместо bool вы также можете возвращать другие значения или структуры для более информативных ошибок.
В конце концов, ваш файл журнала может быть записан любым классом и должен содержать информацию об ошибках, если они есть.