Подскажите пожалуёста, если я создаю собственный класс ошибок, всегда ли он должен быть производным от std::exception?
Я решил написать класс rational, представляющий смешанное число. В результате сложения может призойти переполнение, или может быть попытка присвоить знаменателю нулевое значение — всё это исключительные ситуации. Я создал простую иерархию классов ошибок, базовый класс содержит только номер ошибки, а все производные — функцию-член, возвращающие этот номер. Мне кажется, такая система удобна в отладке, но стоит ли её оставлять как есть? Может лучше наследовать базовый класс от std::exceptin? Просто все строки, возвращаемые функцией what занимают места намного больше, чем сам экземпляр класса.
//Классы ошибок
//базовый класс для всех ошибок при действиях со смешанными числамиclass rational_exception
{
protected:
unsigned short err_code;
public:
unsigned short get_err_code(){return err_code;}
};
//базовый класс ошибок переполнения
//(результат операции не может быть записан в unsigned long long) class overflow : public rational_exception
{
};
//переполнение, возникшее в результате сложения двух чисел типа unsigned long longclass addition_overflow : public overflow
{
public:
addition_overflow(){err_code = 0;}
explicit addition_overflow(unsigned char err_num){err_code = err_num;}
};
//переполнение, возникшее в результате умножения двух чисел типа unsigned long longclass multiplication_overflow : public overflow
{
public:
multiplication_overflow(){err_code = 0;}
explicit multiplication_overflow(unsigned char err_num){err_code = err_num;}
};
//переполнение, возникшее в результате преобразования строки в чисело типа unsigned long longclass recognition_overflow : public overflow
{
public:
recognition_overflow(){err_code = 0;}
explicit recognition_overflow(unsigned char err_num){err_code = err_num;}
};
//базовый клас ошибок неверного ввода class invalid_in : public rational_exception
{
};
//в результате операции ввода была попытка присвоить знаменателю 0 class null_denominator : public invalid_in
{
public:
null_denominator(){err_code = 0;}
explicit null_denominator(unsigned char err_num){err_code = err_num;}
};
//ошибка ввода - введённая строка не может быть распознана как смешанное число class is_not_rational_in : public invalid_in
{
public:
is_not_rational_in(){err_code = 0;}
explicit is_not_rational_in(unsigned char err_num){err_code = err_num;}
};
//базовый клас ошибок конструкторов class invalid_constr_arg : public rational_exception
{
};
//В конструктор передан 0 для присвоения знаменателюclass null_denominator_in_constroctor : public invalid_constr_arg
{
public:
null_denominator_in_constroctor(){err_code = 0;}
explicit null_denominator_in_constroctor(unsigned char err_num){err_code = err_num;}
};
Здравствуйте, Mak_71_rus, Вы писали:
M__>Здравствуйте, форумчане!
M__>Подскажите пожалуёста, если я создаю собственный класс ошибок, всегда ли он должен быть производным от std::exception?
M__>Я решил написать класс rational, представляющий смешанное число. В результате сложения может призойти переполнение, или может быть попытка присвоить знаменателю нулевое значение — всё это исключительные ситуации. Я создал простую иерархию классов ошибок, базовый класс содержит только номер ошибки, а все производные — функцию-член, возвращающие этот номер. Мне кажется, такая система удобна в отладке, но стоит ли её оставлять как есть? Может лучше наследовать базовый класс от std::exceptin? Просто все строки, возвращаемые функцией what занимают места намного больше, чем сам экземпляр класса.
Мое мнение — наследовать нужно. Это считается хорошим тоном. Если вы пишете библиотеку, то такое поведение является естественно ожидаемым.
А такая иерархия ошибок не слишком сложна? Может просто оставить класс переполнения, неверного ввода и нулевого знаменателя (всего три вместо этой кучи)?
Здравствуйте, Mak_71_rus, Вы писали:
M__>А такая иерархия ошибок не слишком сложна? Может просто оставить класс переполнения, неверного ввода и нулевого знаменателя (всего три вместо этой кучи)?
По-моему проще сделать один класс, а конкретный тип исключения передавать как константу в конструктор. Проще в одном catch-e ловить исключение и проверять тип, чем писать туеву хучу catch-ей.
Наследовать от std::exception не обязательно. Но если унаследовать — то ловить потом будет проще
enum
{
kErrorAdditionOverflow,
kErrorMultiplicationOwerflow,
//.....
};
class rational_exception: public std::exception
{
public:
explicit rational_exception(int k): code(k) {}
int const code;
};
Здравствуйте, Mak_71_rus, Вы писали:
M__>Здравствуйте, форумчане!
M__>Подскажите пожалуёста, если я создаю собственный класс ошибок, всегда ли он должен быть производным от std::exception?
Никому он ничего не должен, можно вызвать ошибку любого класса, и обработать любую
M__>Я решил написать класс rational, представляющий смешанное число. В результате сложения может призойти переполнение, или может быть попытка присвоить знаменателю нулевое значение — всё это исключительные ситуации. Я создал простую иерархию классов ошибок, базовый класс содержит только номер ошибки, а все производные — функцию-член, возвращающие этот номер. Мне кажется, такая система удобна в отладке, но стоит ли её оставлять как есть? Может лучше наследовать базовый класс от std::exceptin? Просто все строки, возвращаемые функцией what занимают места намного больше, чем сам экземпляр класса.
Как правило, what не размножается, ибо это все-таки указатель, и если не извращаться (ога, это сложно в обработчике), то проблем не будет. Но вот в плане отладки номера ошибок — не самое удобное. Посмотрите boost::exception, вероятно одним велосипедом меньше станет.
Переубедить Вас, к сожалению, мне не удастся, поэтому сразу перейду к оскорблениям.
M__> Может лучше наследовать базовый класс от std::exceptin? Просто все строки, возвращаемые функцией what занимают места намного больше, чем сам экземпляр класса.
лучше даже от std::runtime_error, наследника std::exception.
Сама идея экономить горстку байтиков в случае "шеф, усё пропало" (исключительной! ситуации) -- выглядит непонятной.
Здравствуйте, kpcb, Вы писали:
K>Наследовать от std::exception не обязательно. Но если унаследовать — то ловить потом будет проще
K>а ловить его так K>
Mak_71_rus wrote: > Подскажите пожалуёста, если я создаю собственный класс ошибок, всегда ли > он должен быть производным от std::exception?
Не должен. Не обязан. Но если ты его унаследуеш от стандартного std::exception
тебе и твоим коллегам будет легче жить. Просто потому что это стандартная
штука и одним catch можно обработать одинаково все исключения.
Не наследовать от std::exception имеет смысл только в каком-то старом
коде, расчитанном на компиляторы, живущие по старым стандартам.
> возвращающие этот номер. Мне кажется, такая система удобна в отладке, но > стоит ли её оставлять как есть? Может лучше наследовать базовый класс от > std::exceptin? Просто все строки, возвращаемые функцией what занимают
Конечно же только базовый.
> места намного больше, чем сам экземпляр класса.
Здравствуйте, pvirk, Вы писали:
P>Я извиняюсь, а где здесь используется то, что было наследование? Что проще ловить стало?
этот catch ловит и другие исключения, возникшие в коде. Что совсем неплохо
Здравствуйте, pvirk, Вы писали:
P>Здравствуйте, kpcb, Вы писали:
K>>Наследовать от std::exception не обязательно. Но если унаследовать — то ловить потом будет проще
K>>а ловить его так K>>
P>Я извиняюсь, а где здесь используется то, что было наследование?
Наследование используется вот здесь:
catch(rational_exception const& ex)
P> Что проще ловить стало?
Своё собственное исключение. Если его не обрабатывать то на х оно нужно. Писать отдельный catch для кучи пораждённых классов — это маразм (ИМХО).
— Классы исключений не обязаны наследовать стндартный класс std::exception (это понятно)
— Принято и считается хорошим тоном наследовать класс ошибок (прямо или через другие классы) от std::error
— Все мои классы ошибок описывают ошибки времени выполнения, поэтому их логично наследовать от std::runtime_error
Здравствуйте, Sni4ok, Вы писали:
S>Здравствуйте, Mak_71_rus, Вы писали:
M__>>Я решил написать класс rational, представляющий смешанное число.
S>а чем не устроили существующие варианты, например boost::rational ?
Тем, что он уже написан, а я просто учюсь объектно-ориентированному программированию, нужно что-то самому написать, не очень сложное, зато как следует!
Здравствуйте, Mak_71_rus, Вы писали:
S>>чем лучше, почему не от logic_error или не от domain_error ?
M__>Ну это логично хотя бы потому, что все классы свидетельствуют именно об ошибках времени выполнения.
аха, а такой вот код, это ошибка выполнения- или таки не совсем?
Здравствуйте, Sni4ok, Вы писали:
S>>>чем лучше, почему не от logic_error или не от domain_error ?
M__>>Ну это логично хотя бы потому, что все классы свидетельствуют именно об ошибках времени выполнения.
S>аха, а такой вот код, это ошибка выполнения- или таки не совсем?
S>
S>rational<int> a(10,0);
S>
А вот тут надо различать два разных момента: первый — это время, когда ошибка была допущена, второй — это когда она обнаруживается. В том месте, где ошибка обнаруживается, об истинных причинах ее возникновения как правило ничего не известно. И исключения как раз и являются средством реакции на ошибки в момент обнаружения. Ошибка, приведенная тобой в качестве примера, бесспорно, была допущена программистом на этапе кодирования, но обнаружится эта ошибка на этапе выполнения. Поэтому вполне естественно, что код, обнаруживший эту ошибку сообщит о ней исключением типа runtime_error или производным от него.
--
Справедливость выше закона. А человечность выше справедливости.
Здравствуйте, rg45, Вы писали:
R> Поэтому вполне естественно, что код, обнаруживший эту ошибку сообщит о ней исключением типа runtime_error или производным от него.