Стандартный способ в C ++ для определения класса исключений и создания исключений - PullRequest
12 голосов
/ 28 мая 2011

Я хочу создать класс с функциями, которые могут выдавать исключения, которые я хочу перехватывать, когда использую его.Я наследую my_exception от стандартного класса исключений.Я реализую функцию what (), чтобы она возвращала строку, которая хранится в закрытой строковой переменной

. Я думаю, что было бы лучше определить исключение как вложенный класс, как это делается в библиотеке iostream сios_base :: fail.

В чем я менее уверен, так это где и как я должен определить объект my_excpetion.Хотелось бы увидеть внутренний код функций iostream и посмотреть, как они это сделали.Я думал о нескольких вариантах:

  1. Для каждой причины исключения я могу определить статический экземпляр my_exception с помощью конструктора, который получает строку и сохраняет ее в моем личном указателе строки.

  2. Для каждой причины исключения я могу определить другой класс, который наследуется от my_exception, и реализовать что-то как функцию, которая возвращает константную строку (причина).Я могу держать экземпляр каждого из этих подклассов исключений, или бросить тип.Кстати, когда мы обычно выбрасываем тип, а не экземпляр?

  3. Я думаю, это неправильно: каждый раз, когда я хочу вызвать исключение, создать новое исключение my_exception с конструктором, который получает строку,Это сделано в Java, но, как я понимаю, это будет проблематично в C ++, потому что исключение должно быть где-то удалено.Правильно?

Я думаю, что 1-й является правильным, не так ли?Есть еще стандартные варианты?

Большое спасибо!

Ответы [ 3 ]

18 голосов
/ 28 мая 2011

Краткий ответ: вы захотите выбросить исключения как объекты, а не как указатели. Вы будете ловить их как ссылки.

Более длинный ответ: все перечисленные варианты действительны. В общем, причина, по которой вы захотите бросить объект, а не указатель, заключается в том, что вы делаете выбор для себя и своих клиентов при обнаружении исключения.

Если вы поймаете по указателю, catch (my_exception* e), то, глядя на него, вы не знаете, следует ли вам удалить память или нет.

Если вы ловите по значению, catch (my_exception e), тогда у вас есть риск нарезки, если объект исключения оказывается базовым классом с некоторыми другими производными классами.

Отлов по ссылке не имеет ни одной из этих проблем. Если вы напишите catch (my_exception& r), тогда вы сможете ловить полиморфные объекты, и вам не придется беспокоиться об освобождении памяти.

Итак, чтобы ответить на другой вопрос, когда вы бросаете, просто бросьте временный объект: throw my_exception(). Это создает временный объект, который (вероятно) копируется при броске, перехватывается по ссылке и автоматически уничтожается, когда он выходит из области видимости в конце вашего блока catch. (На самом деле это еще одно преимущество функции «поймать по ссылке» по сравнению с «поймай по значению», так как «поймай по значению» создает еще одну копию при ее обнаружении.)

Что касается других ваших производных классов исключений, то это выбор стиля. Вывод из my_exception с другой реализацией what () довольно стандартен. Я бы не сказал, что вам нужно увлекаться хранением строк или экземпляров в статических объектах - они маленькие, и их создание займет практически не время по сравнению с процессом разматывания стека при возникновении исключения.

12 голосов
/ 28 мая 2011

Если вы наследуете от std :: runtime_error, вам не нужно определять свой собственный член для хранения строки.Это сделано для вас в std :: exception (основа std :: runtime_error).Не определено, как исключение хранит строку, но оно всегда должно работать.

#include <stdexcept>
#include <string>

struct MyException: public std::runtime_error
{
    MyException(std::string const& message)
        : std::runtime_error(message + " Was thrown")
    {}
};
0 голосов
/ 28 мая 2011

Ничего плохого в любом из ваших вариантов. Номер 3 в порядке, пока вы создаете локальную переменную и не используете new, потому что нет необходимости удалять объект исключения - он будет уничтожен, как только вы бросите. Вам нужно будет создать конструктор копирования и оператор копирования, потому что выброшенное исключение на самом деле будет копией той, которую вы даете оператору throw.

Вариант 1 будет необычным, поскольку обычно он не нужен.

Для варианта 2 вы должны создать экземпляр класса для выброса. Нельзя бросить тип, только экземпляр типа.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...