Исключения С++; int или std:: exception?
Я пишу некоторые библиотечные функции, которые должны выполнять некоторые сообщения об ошибках. Я хочу использовать исключения, а не возвращаемые значения.
Я написал свои функции, которые генерируют исключения int.
Например:
if(strm->atEnd()){
// unexpected end of stream
throw -2;
}
Мой вопрос в том, является ли этот метод ОК? Или я должен исключить исключение из std:: exception?
Каким образом лучше избавиться от std:: exception? (Помимо возможности использования catch (std:: exception & e))
Выбрасывает int исключения из-за плохой практики? (все значения throw int указаны в комментариях doxygen)
Я не могу найти причину, почему (в этом случае) я должен бросать объект.
Ответы
Ответ 1
Вы должны выбросить исключение на основе std::exception
.
throw std::runtime_error("unexpected end of stream")
Просто подумайте, насколько это проще для catch
, log и т.д. Он также позволяет удалить тщетный комментарий и удалить магический номер из вашего кода!
Это сообщение затем может быть отправлено конечному пользователю, чтобы дать им надежду на устранение проблемы. Они не могут читать комментарии в вашем коде (не будут читать ваш вывод Doxygen), и они вряд ли будут знать, что означает "-2".
Ответ 2
Исключение - для исключительного поведения. Это последнее, что вам нужно беспокоиться об оптимизации!
Дональд Кнут сказал:
Мы должны забыть о небольшой эффективности, скажем, около 97% времени: преждевременная оптимизация - корень всех злых
Кроме того, исключение объекта может содержать информацию об ошибке.
Например, у вас есть исключение, означающее, что файл не может быть прочитан. Если вы выбросите исключение объекта, объект может нести имя файла, которое вы не можете иметь с помощью ints.
Если источник возникновения неизвестен (в глубине вашего стека), и никто его не поймает, будет легче отлаживать программу, если исключение является объектом с соответствующей информацией.
Ответ 3
Мой вопрос в том, является ли этот метод ОК?
Подумайте о читаемости. Не было бы
throw CUnexpectedEndOfStream();
более читабельна, чем
throw -2
?
И во многих случаях не будет видно, что экземпляр CUnexpectedEndOfStream, добавленный в отладчик, означает TONS больше, чем -2. Это не говоря уже о том, что CUnexpectedEndOfStream может хранить кучи полезной информации о проблеме, например, сказать, что файл не может быть прочитан и, возможно, больше информации о характере проблемы.
Или мне нужно исключить исключение из std:: exception?
Наследование из std::exception
может оказаться полезным, если вы решите организовать другие исключения. Это удобный базовый класс, который может использовать клиентский код. Также в зависимости от исключения вы можете использовать std:: runtime_error.
Выбрасывает int исключения из-за плохой практики? (все значения throw int указаны в комментариях doxygen)
Кто сказал, что это плохая практика? Бросание исключений - отличный способ справиться с исключительными ситуациями. Большинство исключений, которые я бросаю, из-за чего-то, что может быть предотвращено кодом клиента, выполняющим то, что он должен был... пользователь моего кода нарушил контракт. Но многие другие исключительные ненормальные случаи также существуют как ошибки ОС, заполнение дисков и т.д. Все, что не является частью ваших программ, нормальное течение. Что еще более важно, поскольку они не являются частью вашей обычной программы, вам не нужно беспокоиться о производительности.
В качестве примера я однажды использовал исключения, чтобы вызвать, что произошел определенный синтаксический анализ сообщения. Тем не менее, я обнаружил, что эти ошибки синтаксического анализа часто повторялись до такой степени, что я начал обрабатывать исключения и исправлять ввод и повторный набор проблем. Через некоторое время я понял, что более читаемым решением было бы исправить проблему непосредственно в коде анализа и прекратить рассматривать его как исключительный случай. Весь код, который принял решения о синтаксическом анализе, был возвращен в одном месте, и я не бросал исключений, таких как пьяный матрос, выкидывающий проклятые слова.
Ответ 4
Вы должны выдать исключение, полученное из std::exception
, например. std::runtime_error
.
В большинстве существующих кодеков предполагается, что обычное исключение С++ - это std::exception
, а все остальное - это "жесткое исключение", которое должно распространяться до очень высокого уровня управления, например main
.
Например, существующий код, скорее всего, не сможет зарегистрировать какое-либо разумное сообщение для вашего int
. Это будет просто "неизвестный тип исключения".
Приветствия и hth.,
Ответ 5
Почему бы вам не сделать это:
if(strm->atEnd()){
// unexpected end of stream
throw std::exception("-2");
}
throw -2
является плохим, потому что тип -2
не является ни std::exception
, ни выводится из std::exception
.
Или вам лучше написать свой собственный класс:
class FileException : std::exception
{
//...
};
И затем
throw FileException("Unexpected end of stream");
что более читаемо.
Ответ 6
В теории вы всегда можете бросать сложные исключения, но в большинстве случаев вам может и не понадобится.
Мой вопрос в том, является ли этот метод ОК?
Да и Нет. В теории вы могли бы сделать это во что бы то ни стало, но это не очень хорошая практика (см. ниже). Однако, если вы все еще делаете это, по крайней мере, я бы предложил #define
коды ошибок для лучшей читаемости.
#define UNEXPECTED_END_OF_STREAM -2
if(strm->atEnd()){
// unexpected end of stream
throw UNEXPECTED_END_OF_STREAM;
}
Очень необычно бросать числа -ve как коды ошибок/исключение, и это, скорее всего, будет недооценивать. Фактически люди будут нахмуриться, бросая исключение int
, даже если оно будет делать именно то, что вы хотите сделать (в большинстве случаев, по крайней мере), то есть поймать ошибку.
Наиболее распространенным исключением, которое я бросаю, является std:: runtime_error ( "Моя ошибка" ), который является маскировкой для выброса простого строкового исключения. В большинстве случаев основные исключения - все, что нам нужно, обнаруживать ошибку и возвращать.
Помните, какое бы исключение вы ни выбрали, вы всегда получаете преимущество блока try-catch
, который действительно является сердцем обработки исключений. То же самое верно, если вы выбрали исключение int
. Я полагаю, что int
скорее всего укажет код ошибки. Иногда это может потребоваться, если вы используете исключения во внутренних документах dll, но хотите вернуть коды ошибок вместо внешнего мира. Я думаю, что ваш вопрос действительно сводится к этой проблеме, могу ли я сделать исключение int
и должен ли я?
Также обратите внимание, пока вы можете это сделать, однако исключение int
не самое элегантное. Зачем? Поскольку int
- это время примитивных данных, и вы хотите думать, что Object ориентирован на С++, поэтому лучше бросить объект. Это может означать, что вам может потребоваться инкапсулировать код ошибки int
в объект и выбросить этот объект. Возможно, когда вы это сделаете, может быть хорошей идеей добавить строку, описывающую ошибку. Вот что я сделал в прошлом. Я получил свой класс исключения из std::runtime_error
и добавил код ошибки.
class my_error : public std::runtime_error
{
public:
my_error(std::string const& msg, int code);
virtual ~my_error(void);
// the error code which you want to throw
int errCode;
};
Теперь, когда вы хотите выбросить код исключения/ошибки, выполните следующие действия:
throw my_error("this is my own error!", UNEXPECTED_END_OF_STREAM );