Исключение
есть несколько файлов, которые мы хотим открыть и прочитать оттуда,
есть фун-ии, которые открывают файл и читают оттуда, эти фун-ии все разные: то есть есть f1("sdsd.txt"), f2("sas.txt"),f3("fdfdf.txt") и т.д., то есть они открывают и читают по-разному(это так требуется).
Нужно вызвать эти фун-ии в другой фун-ии, т.е:
void MyFun()
{
f1("sdsd.txt");
f2("sas.txt");
f3("fdfdf.txt");
...
}
Файлов в указанном месте может не быть.
Вопрос:как с помощью "исключений" записать фун-ию MyFun() правильно, чтобы каким-то образом оповещать пользователя о несуществующем файле и продолжить выполнение фун-ии MyFun()?
Выдавать сообщение в окно или в cerr не предлагать, т.к. предполагается, что фун-ия MyFun() - из какой-то библиотеки(например:в математической библиотеке, написанной сторонним разработчиком).
throw остановит выполнение функции. Читай вопрос еще раз.
Попробовать создать новый поток, в котором выводить исключения, а функцию продолжать в основном.
Rebbit подсказал тебе правильное решение - использовать callback-функцию или другой вид наблюдателя.
Делаетцо примерно так:
{
try
{
f1("sdsd.txt");
}
catch (exception&)
{
cerr << "FATAL ERROR!!!" << endl;
}
// Здесь продолжает исполняться MyFun
}
Внутри f1 при невозможности открыть файл должно быть начертано "throw exception()", что вызывает исключение с объектом класса exception, описывающего это исключение.
Читаем топик:
Вопрос:как с помощью "исключений" записать фун-ию MyFun() правильно, чтобы каким-то образом оповещать пользователя о несуществующем файле и продолжить выполнение фун-ии MyFun()?
Исключения нужны лишь в исключительных/критичных ситуациях.
В данном случае они не нужны.
Поэтому, на собеседовании я бы так и сказал: в данном случае исключение здесь - лишнее.
Более того, если применить исключения так, как показал Ramon (я понимаю, что это всего лишь "простой пример"), то остальная часть программы никогда не узнает о случившимся. А как уже говорилось исключения предназначены для критических ошибок, оказывающих влияние на всю программу.
Раз уж об етом завели розговор .....
Мне приходилось делать програму которая должна била роботать с минимальным вмешательством человека (админа). Ето был промежуточный уровень (прокладка) между апликейшеном который генерировал документы и сервисом который их принимал, давал подтверждения, оповещал об ошибках в документе и т.п. Собственно прокладка занималась суто транспортом, принимала документ от апликейшена, отправляла сервису, менялась с ним промежуточными сообщениями пока не получала окончательный результат, который отправлялся апликейшену.
Прикращение роботы прокладки изза сбоя был недопустим.
Я старался перехватить все ексепшены которые могли быть визваны обектами которые я юзал, но в то же время я генерировал и свои исключения, которие тоже перехватывал. При возникновении ексепшена я старался както возобновить роботу моей прокладки (ну к примеру еще раз отослать документ) или если возобновление было недопустимым - писал лог-ошибок.
Мой вопрос.
Завершение роботы програмы не происходило, но роботать с ексепшенами мне было довольно удобно. Я плохо спроектировал свою програму или же такое использование ексепшенов вполне приемлимо ?
Но при работе с исключениями:
1. Замедляется выполнение программы. (много времени тратится на посылку исключения, в отличие от простого перехвата возвращаемых значений функции).
2. Надо иметь в виду что при бросании искльчения все локальные объекты в текущем блоке разрушаются.
3. Плохо бросать искючения в деструкторах (следует искючить возможность бросания исключения в исключении).
Мой вопрос.
Завершение роботы програмы не происходило, но роботать с ексепшенами мне было довольно удобно. Я плохо спроектировал свою програму или же такое использование ексепшенов вполне приемлимо ?
Сложно сказать.
Все зависит от возможности и эффективности вариантов решения.
Если исключения навязаны сторонними библиотеками, то о чем тут говорить. Если твои исключения хорошо вписывались в схему обработки исключений сторонних библиотек, то смысла разделять обработку на свои/чужие не вижу. Если же тебе пришлось делать несколько схем обработки исключений, то тогда возможно архитектура не совсем верна.
Что я подразумеваю под схемой обработки исключений.
В моем понимании обработка исключений должна происходить как можно выше по иерархии вызовов. Т.е. грубо говоря идеальное место - это функция main. :)
Конечно, это не всегда возможно и не всегда целесообразно. Например (отвлеченный,довольно абстрактный пример, не берите его за основу реализации), мы имеем ряд взаимозаменяемых и взаимодополняемых датчиков. Есть программа управляющая на основе показаний этих датчиков какими-то органами управления. Допустим, что при выходе датчика из строя происходит исключение. Естественно, что обработка этих исключений должна происходить в блоке предоставляющем показания с датчиков, а не в основном или др. блоках программы, т.к. мы условились, что датчики взаимозаменяемы и блок может сам определить, как ему теперь снимать информацию для предоставления основной системе. Т.о. мы имеем как-бы враппер (обертку) над этими исключениями, отделяющую логику работы в исключительных ситуациях от основной системы. Т.е. когда системе нет особого дела (до последнего живого датчика), что там происходит, ей главное информация.
Если же выход датчика из строя оказывает влияние на всю систему, то эту обработку наряду с обработкой выхода из строя остальных критичных узлов системы лучше вынести на уровень всей системы. Т.о. мы имеем общий механизм обработки однотипных (критичных ко всей системе) исключений.
Теперь если ты пристыковываешь свой блок к такой системе, и выход его из строя так же критичен для всей системы, есть смысл завести его в общую схему обработки исключений.
если я не прав, то почему?
Если же тебе пришлось делать несколько схем обработки исключений, то тогда возможно архитектура не совсем верна.
Роботало у меня примерно так. Структуру мне помагали делать, поскольку сам бы я сделал намного хуже. Обмен шел по нттп. Каждый документ обробатывался в отдельном потоке поскольку сервис их обрабатывал асинхронно. Робота с документом разделялась на несколько стадий: отправка, ожидание ответа (на етой стади я обращался с запросом на ответ, а сервис или давал мне ответ или оповещал через какое время мне обратится к нему еще раз), оброботка ошибок в посланом документе, потверждение получения мною ответа, удаление документа и всей супутствуещей инфы с сервиса. На каждой стадии мной генерировался хмл определенной структуры и парсался ответ сервиса.
В верхнем цикле каждого потока в зависимости от стадии обмена вызывался нужний обэкт которий генерировал, отправлял и парсил хмл.
Исключения у меня были двох типов:
(возможно я плохо подобрал названия для етих типов)
1. Внешние - не соединился с сервисом, превышен интервал ожидания....
В таком случае я делал таймаут и несколько раз пробовал повторить.
Если исключение повторялось несколько раз, обрабатывал так же как и второй тип исключений.
2. Внутренние (те которие я генерировал) - сервис не понял мой хмл или я не понял ответ сервиса.
Документация по стуктуре документов была не идеальной и схем для валидацыи у меня не было. Я использовал етот тип исключений для того чтоб отследить правильно ли я генерю и парсаю документы.
Если происходило такое исключение все данные сохранялись и поток останавливался. Я прекращал выполнение всего, поскольку мне надо было поправить мою програму и начать с того места где я закончил обмен (условно конечно. Я не делал джамп на то место где произошла ошибка, просто оставлял данные нетронутыми).
{
Numbers.clear(); Messages.clear();
for (int i = 1; i <= 3; i++)
{
try
{
switch (i)
{
case 1:
f1("sdsd.txt");
i++;
case 2:
f2("sas.txt");
i++;
case 3:
f3("fdfdf.txt");
i++;
} // switch
}
catch (exception &error)
{
Numbers.push_front (i); // запоминаем номер функции
Messages.push_front (error.message); // запоминаем текст ошибки
}
} //for
} // функция
Цепочка функций начинает поочерёдно выполняться внутри блока try.
Когда в одной функции происходит ошибка, её номер и текст запоминаются в списках. После чего управление передаётся в начало цикла (в начало блока try), а переменная цикла позволяет выполнить переход к очередной функции.
но НИКОГДА НЕ ПИШИТЕ TRY\CATCH ВНУТРИ ЦИКЛОВ
исключения это в люом языке программирования самая тормозящая конструкция. поэтому если писать try\catch внутри длинных циклов, то производительность будет резко падать. лучше писать цикл в блоке try\catch
Данной ситуации следует избегать, но иногда она неизбежна.
Пример:
{
bool connected = true;
try
{
Устанавливаем соединение с сервером БД;
}
catch (...)
{
connected = false;
}
while (!connected)
{
Ждем 5 секунд;
connected = true;
try
{
Устанавливаем соединение с сервером БД;
}
catch (...)
{
connected = false;
}
}
try
{
Работаем с БД, выполняем различные запросы;
}
catch (...)
{
// Если на сервер пролили кофе, соединение разрывается и происходит исключение при попытке выполнения очередного запроса. Но ничего - мы подождем, пока соединение снова установится и возобновим работу в следующей итерации цикла...
}
}