Стандартный способ в С++ для определения класса исключения и исключения исключений
Я хочу создать класс с функциями, которые могут генерировать исключения, которые я хочу поймать, когда я его использую.
Я унаследовал my_exception от стандартного класса исключения.
Я реализую функцию what() так, чтобы она возвращала строку, которая хранится в частной строковой переменной
Я думаю, было бы лучше определить исключение как вложенный класс, как это было сделано в iostream-библиотеке с ios_base:: failure.
Что я менее уверен, где и как я должен определить объект my_excpetion. Мне хотелось бы видеть внутренний код функций iostream и посмотреть, как они это сделали.
Я подумал о нескольких вариантах:
-
Для каждой причины исключения я могу определить статический экземпляр my_exception, с конструктором, который получает строку и сохраняет ее в своем личном указателе строки.
-
По каждой причине исключения я могу определить другой класс, который наследует от my_exception и реализует то, что в качестве функции возвращает постоянную строку (причина).
Я могу хранить экземпляр каждого из этих подклассов исключений или бросать тип.
BTW, когда мы обычно бросаем тип, а не экземпляр?
-
Я предполагаю, что это неправильно: каждый раз, когда я хочу создать исключение, создайте новое my_exception с конструктором, который получает строку. Это делается на Java, но, как я понимаю, это будет проблематично в С++, потому что исключение должно быть удалено где-то. Правильно?
Я думаю, что первый - правильный, не так ли?
Есть ли более стандартные варианты?
Большое спасибо!
Ответы
Ответ 1
Короткий ответ: вы захотите исключить исключения как объекты, а не как указатели. Вы поймаете их как ссылки.
Более длинный ответ: все доступные вам параметры. В общем, причина, по которой вы захотите бросить объект, а не указатель, заключается в том, что вы выбираете себя и своих клиентов, когда исключение поймано.
Если вы поймаете указатель, catch (my_exception* e)
, то вы не знаете, глядя на него, следует ли вам удалить память или нет.
Если вы поймаете значение, catch (my_exception e)
, тогда у вас есть риск нарезки, если объект исключения окажется базовым классом с некоторыми другими производными классами.
Ловля ссылками не имеет ни одной из этих проблем. Если вы пишете catch (my_exception& r)
, вы можете поймать полиморфные объекты, и вам не нужно беспокоиться о том, чтобы освободить память.
Итак, чтобы ответить на ваш другой вопрос, когда вы бросаете, просто бросайте временный объект: throw my_exception()
. Это создает временный объект, который (вероятно) скопирован при броске, пойман по ссылке и автоматически уничтожается, когда он выходит из области видимости в конце вашего блока catch. (Это на самом деле другое преимущество catch-by-reference над catch-by-value, так как catch-by-value создает еще одну копию, когда она попадает.)
Что касается других, производных классов исключений, это выбор стиля. Вывод из my_exception с другой реализацией() является довольно стандартным. Я бы не сказал, что вам нужно увлекаться хранением строк или экземпляров в статических объектах - они небольшие, и для их построения требуется практически не время, чем процесс разворачивания стека при вызове исключения.
Ответ 2
Если вы выходите из 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")
{}
};
Ответ 3
Ничего плохого в ваших вариантах. Номер 3 в порядке, если вы создаете локальную переменную и не используете new
, потому что нет необходимости удалять объект исключения - он будет уничтожен, как только вы выбросите. Вам нужно будет создать конструктор копирования и оператор копирования, потому что брошенное исключение на самом деле будет копией того, которое вы передаете оператору throw
.
Вариант 1 был бы необычным, потому что он обычно не нужен.
Для варианта 2 вы должны создать экземпляр класса для броска. Невозможно бросить тип, только экземпляр типа.