Исключения С++; 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 );