Подскажите пожалуёста, если я создаю собственный класс ошибок, всегда ли он должен быть производным от 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 или производным от него.
S>аха, а такой вот код, это ошибка выполнения- или таки не совсем?
S>
S>rational<int> a(10,0);
S>
Имеется в виду присвоение 0 знаменателю? Ну это же ошибка времени выполнения. Сравните:
int mynull = 0;
int notnull = 5;
int result = notnull / mynull;
Возникнет ошибка:
Необработанное исключение в "0x00921a98" в "Class_rational.exe": 0xC0000094: Integer division by zero.
Разве это не ошибка времени выполнения?
Sni4ok:
S>аха, а такой вот код, это ошибка выполнения- или таки не совсем?
S>
rational<int> a(10,0);
Хороший вопрос. В данном случае передача нуля в качестве знаменателя — это нарушение предусловий (знаменатель должен быть отличен от нуля). В концепции Design by Contract (DbC) это будет логической ошибкой программы, а не ошибкой времени выполнения.
Кстати, для справки:
C++03 — 19.1.1
The class logic_error defines the type of objects thrown as exceptions to report errors presumably detectable before the program executes, such as violations of logical preconditions or class invariants.
C++03 — 19.1.6:
The class runtime_error defines the type of objects thrown as exceptions to report errors presumably detectable only when the program executes.
Впрочем, недостатки использования std::logic_error тут как-то уже обсуждались. Простого assert-а может быть вполне достаточно.
S>вы путаете мягкое со сладким, bad_alloc — это ошибка времени исполнения или нет?
В моём понимании да, но, честно говоря, я в этом не рулю.
Ещё обстоятельство:
cout << "Введите знак: '+' или '-': ";
cin >> sign;
switch (sign)
{
case'+':
s = rational::plus;
break;
case'-':
s = rational::minus;
break;
default:
cout << "Вы ввели знак неверно!" << endl;
continue;
}
cout << "Введите первое число: ";
cin >> a;
cout << "Введите второе число: ";
cin >> b;
cout << "Введите третье число: ";
cin >> c;
rational r3(s, a, b, c);
cout << "Результат вызова второго конструктора: " << r3 << endl;
Тут знаменатель вводит пользователь. И тогда то это точно ошибка времени выполнения. Её невозможно предусмотреть до запуска программы и получения соответствующих значений от пользователя.
Здравствуйте, Mak_71_rus, Вы писали:
M__>А такая иерархия ошибок не слишком сложна? Может просто оставить класс переполнения, неверного ввода и нулевого знаменателя (всего три вместо этой кучи)?
А ты можешь представить себе ситуацию, когда эти ошибки нужно обрабатывать отдельно, т.е
try{ some code}
catch(const addition_overflow&){
действие 1
}
catch(const multiplication_overflow&){
действие 2
}
?
Если да, то такая иерархия оправдана, иначе нет.
Mak_71_rus:
M__>Тут знаменатель вводит пользователь. И тогда то это точно ошибка времени выполнения. Её невозможно предусмотреть до запуска программы и получения соответствующих значений от пользователя.
По DbC нарушение предусловий — это всегда программная ошибка. Тот, кто вызывает функцию, обязан следить за тем, чтобы в неё передавались корректные аргументы. Если при правильных аргументах функция не может выполнить свою задачу в силу внешних по отношению к программе факторов, то это уже либо ошибка времени выполнения, либо запланированное прерывание её работы.
Впрочем, некоторые сформулированные предусловия могут быть упрощены (во избежание чрезмерной сложности), тогда невозможность выполнения задачи может целиком зависеть от переданных аргументов. Но здесь упрощать предусловия я смысла не вижу ("знаменатель должен быть отличен от нуля" — это очень простое предусловие, которое легко проверить до вызова функции).
Здравствуйте, Masterkent, Вы писали:
M>Впрочем, некоторые сформулированные предусловия могут быть упрощены (во избежание чрезмерной сложности), тогда невозможность выполнения задачи может целиком зависеть от переданных аргументов. Но здесь упрощать предусловия я смысла не вижу ("знаменатель должен быть отличен от нуля" — это очень простое предусловие, которое легко проверить до вызова функции).
А зачем вообще использовать контрактное программирование в данном случае, только ради того, чтобы не проверять несколько условий? Зато если его не использовать класс будет использовать проще.
Mak_71_rus:
M__>А зачем вообще использовать контрактное программирование в данном случае, только ради того, чтобы не проверять несколько условий?
А что, на твой взгляд, более естественно: проверять ввод на ошибки сразу или же посылать куда-то заведомо некорректные данные и получать отказ?
M__>Зато если его не использовать класс будет использовать проще.
Чем именно проще? Отказ ведь тоже нужно где-то обрабатывать. Причём информация "в функцию передали ноль" (которая может содержаться в твоём исключении) довольно мало о чём говорит (без знания контекста она практически бесполезна).
Здравствуйте, Masterkent, Вы писали:
M>А что, на твой взгляд, более естественно: проверять ввод на ошибки сразу или же посылать куда-то заведомо некорректные данные и получать отказ?
Естественнее сразу, но если это не сделано? Теоретически классом пользуется человек, не имеющий никакого отношения к его разработке. Рарабатывая свою программу, он может допустить ошибку и не проверить отправляемые данные на корректность. Если исключения не будет, то программа будет вести себя непредсказуемо: может упадёт, а может просто выдаст неверные данные. Во втором случае это сложно заметить сразу; а если заметить, то сложно будет найти причину такого поведения программы.
M>Чем именно проще? Отказ ведь тоже нужно где-то обрабатывать. Причём информация "в функцию передали ноль" (которая может содержаться в твоём исключении) довольно мало о чём говорит (без знания контекста она практически бесполезна).
Проще тем, что если эта ошибка не будет обработана вовсе, то хоть при отладке можно понять в чём дело (само имя класса ошибки будет выведено в окне сообщения об ошибке). Понятно, что если функция генерирует исключения, то они должны быть перехвачены, но мали как будет использован класс. И сгенерированное исключение лучше, чем просто падение програмы, неведомо от чего.
А информация "в функцию передали ноль" может всего лишь позволить сообщить пользователю программы, что задача не выполнена, т.к. введены ошибочные данные. А больше ничего сделать не получится: если пользователь сказал: "Дели на ноль", можно только ответить: "Это не возможно", а разделить всё равно не получится.
Подумав над сказанным здесь переделал иерархию классов так, чтобы они, во-первых, наследовались от базового класса ошибок класса rational, а, во-вторых, от соответствующих типов стандартных ошибок:
//Классы ошибок
//базовый класс для всех ошибок при действиях со смешанными числамиclass rational_exception
{
protected:
unsigned short err_code;
public:
unsigned short get_err_code(){return err_code;}
};
//переполнение, возникшее в результате сложения двух чисел типа unsigned long longclass clrational_addition_overflow: public std::overflow_error,
public rational_exception
{
public:
clrational_addition_overflow(unsigned int errnum,
const std::string& message):
std::overflow_error(message){err_code = errnum;}
};
//переполнение, возникшее в результате умножения двух чисел типа unsigned long longclass clrational_multiplication_overflow: public std::overflow_error,
public rational_exception
{
public:
clrational_multiplication_overflow(unsigned int errnum,
const std::string& message):
std::overflow_error(message){err_code = errnum;}
};
//переполнение, возникшее в результате преобразования строки в чисело типа
//unsigned long long (результат операции не может быть записан в unsigned long long) class clrational_recognition_overflow: public std::overflow_error,
public rational_exception
{
public:
clrational_recognition_overflow(unsigned int errnum,
const std::string& message):
std::overflow_error(message){err_code = errnum;}
};
//в результате операции ввода была попытка присвоить знаменателю 0 class clrational_null_denominator: public std::runtime_error,
public rational_exception
{
public:
clrational_null_denominator(unsigned int errnum,
const std::string& message):
std::runtime_error(message){err_code = errnum;}
};
//ошибка ввода - введённая строка не может быть распознана как смешанное числоclass clrational_not_rational: public std::runtime_error,
public rational_exception
{
public:
clrational_not_rational(unsigned int errnum,
const std::string& message):
std::runtime_error(message){err_code = errnum;}
};
//в конструктор передан 0 для присвоения знаменателюclass clrational_constructor_null_denominator: public std::invalid_argument,
public rational_exception
{
public:
clrational_constructor_null_denominator(unsigned int errnum,
const std::string& message):
std::invalid_argument(message){err_code = errnum;}
};
Все исключения класса rational можно перехватывать одним catch. При этом тип определять с помощью typeid::name
Здравствуйте, Mak_71_rus, Вы писали:
M__>Здравствуйте, форумчане!
M__>Подскажите пожалуёста, если я создаю собственный класс ошибок, всегда ли он должен быть производным от std::exception?
M__>Я решил написать класс rational, представляющий смешанное число. В результате сложения может призойти переполнение, или может быть попытка присвоить знаменателю нулевое значение — всё это исключительные ситуации. Я создал простую иерархию классов ошибок, базовый класс содержит только номер ошибки, а все производные — функцию-член, возвращающие этот номер. Мне кажется, такая система удобна в отладке, но стоит ли её оставлять как есть? Может лучше наследовать базовый класс от std::exceptin? Просто все строки, возвращаемые функцией what занимают места намного больше, чем сам экземпляр класса.
Наследовать не обязательно, но ты облегчишь жизнь людям, реюзающим твой код. Обычно после ловли всех конкретных исключений ловятся std::exception, потом — все остальные, через catch(...). Если не хочешь, чтобы твои исключения попали в секцию catch(...), не оставив никакой полезной информации, наследуйся от std::exception (std::runtime_error, std::logic_error, в зависимости от контекста).
Здравствуйте, Masterkent, Вы писали:
M__>>А зачем вообще использовать контрактное программирование в данном случае, только ради того, чтобы не проверять несколько условий?
M>А что, на твой взгляд, более естественно: проверять ввод на ошибки сразу или же посылать куда-то заведомо некорректные данные и получать отказ?
кстати, а как это утверждение соотносится с инкапсуляцией и прочими прелестями? Получается, что нельзя положиться на "черный ящик" из какого-то модуля. Надо четко знать, какие условия необходимы ему для работы и прочее.
Mak_71_rus:
M>>А что, на твой взгляд, более естественно: проверять ввод на ошибки сразу или же посылать куда-то заведомо некорректные данные и получать отказ?
M__>Естественнее сразу, но если это не сделано?
Если проверка должна быть сделана, но не сделана, значит, ошибка в самой программе. Реагировать на программные ошибки можно по-разному: можно вставить assert, можно выбросить исключение, производное от std::logic_error, можно вызвать свой обработчик.
M__>Теоретически классом пользуется человек, не имеющий никакого отношения к его разработке.
Предусловия следует документировать.
M__>Рарабатывая свою программу, он может допустить ошибку и не проверить отправляемые данные на корректность. Если исключения не будет, то программа будет вести себя непредсказуемо: может упадёт, а может просто выдаст неверные данные. Во втором случае это сложно заметить сразу; а если заметить, то сложно будет найти причину такого поведения программы.
Лишние проверки могут негативно сказаться на производительности. Лучшего решения (проверять или не проверять аргументы функции на ошибки) на все случаи не существует.
M>>Чем именно проще? Отказ ведь тоже нужно где-то обрабатывать. Причём информация "в функцию передали ноль" (которая может содержаться в твоём исключении) довольно мало о чём говорит (без знания контекста она практически бесполезна).
M__>Проще тем, что если эта ошибка не будет обработана вовсе, то хоть при отладке можно понять в чём дело
При отладке можно полагаться и на assert-ы. Это вполне стандартное средство отлова программных ошибок.
March_rabbit, Вы писали:
M__>>>А зачем вообще использовать контрактное программирование в данном случае, только ради того, чтобы не проверять несколько условий?
M>>А что, на твой взгляд, более естественно: проверять ввод на ошибки сразу или же посылать куда-то заведомо некорректные данные и получать отказ? M_>кстати, а как это утверждение
Какое именно утверждение? В процитированном я вижу только вопросы.
M_>соотносится с инкапсуляцией и прочими прелестями?
А что не так с инкапсуляцией? И что за прочие прелести?
M_>Получается, что нельзя положиться на "черный ящик" из какого-то модуля.
Почему нельзя?
M_>Надо четко знать, какие условия необходимы ему для работы
M>Если проверка должна быть сделана, но не сделана, значит, ошибка в самой программе. Реагировать на программные ошибки можно по-разному: можно вставить assert, можно выбросить исключение, производное от std::logic_error, можно вызвать свой обработчик.
А какая собственно разница, какое исключение поднимать logic_error или runtime_error? Всё равно перехват с помощбю catch
M>Предусловия следует документировать.
Это само собой.
M>Лишние проверки могут негативно сказаться на производительности. Лучшего решения (проверять или не проверять аргументы функции на ошибки) на все случаи не существует.
Тут придётся выбрать между увеличением производительности и (в моём понимании) удобством использования. Я буду придерживаться концепции: программа должна работать должным образом или сообщить об ошибке. Ни при каких входных данных невозможно получить неверный результат.
M>>>Чем именно проще? Отказ ведь тоже нужно где-то обрабатывать. Причём информация "в функцию передали ноль" (которая может содержаться в твоём исключении) довольно мало о чём говорит (без знания контекста она практически бесполезна).
M__>>Проще тем, что если эта ошибка не будет обработана вовсе, то хоть при отладке можно понять в чём дело M>При отладке можно полагаться и на assert-ы. Это вполне стандартное средство отлова программных ошибок.
Код класса будет уже скомпилирован к моменту отладки кода, использующего класс.
Mak_71_rus:
M>>Если проверка должна быть сделана, но не сделана, значит, ошибка в самой программе. Реагировать на программные ошибки можно по-разному: можно вставить assert, можно выбросить исключение, производное от std::logic_error, можно вызвать свой обработчик.
M__>А какая собственно разница, какое исключение поднимать logic_error или runtime_error?
Ну, если программные и все остальные ошибки ты обрабатываешь одинаково, то для тебя никакой разницы не будет.
M__>>А какая собственно разница, какое исключение поднимать logic_error или runtime_error?
M>Ну, если программные и все остальные ошибки ты обрабатываешь одинаково, то для тебя никакой разницы не будет.
А как надо обрабатывать logic_error? Вот в знаменатель передан 0. Выбрасывается исключение logic_error. И?
Здравствуйте, kpcb, Вы писали:
K>По-моему проще сделать один класс, а конкретный тип исключения передавать как константу в конструктор. Проще в одном catch-e ловить исключение и проверять тип, чем писать туеву хучу catch-ей.
А чем туева хуча captch-ей лучше туевой хучи проверок типа ошибки? И потом, к исключению иногда полезно еще какую-то информацию прикрепить, связаную с ошибкой. Неужели всё в один клас пихать?
Mak_71_rus:
M__>А как надо обрабатывать logic_error?
Это зависит от того какие требования предъявляются к программе в случае обнаружения в ней внутренних ошибок.
M__>Вот в знаменатель передан 0. Выбрасывается исключение logic_error. И?
Например, можно записать отчёт об ошибке в лог-файл, сообщить пользователю об обнаружении внутренней ошибки программы и о том, что программа будет закрыта, а потом убить процесс.
M__>>Вот в знаменатель передан 0. Выбрасывается исключение logic_error. И?
M>Например, можно записать отчёт об ошибке в лог-файл, сообщить пользователю об обнаружении внутренней ошибки программы и о том, что программа будет закрыта, а потом убить процесс.
Ну собственно то же будет и при runtime_error/ Выходит, это чисто формальный вопрос о выборе типа ошибки: runtime или logic, а по сути от этого выбора ничего не зависит.
Mak_71_rus:
M__>>>Вот в знаменатель передан 0. Выбрасывается исключение logic_error. И?
M>>Например, можно записать отчёт об ошибке в лог-файл, сообщить пользователю об обнаружении внутренней ошибки программы и о том, что программа будет закрыта, а потом убить процесс.
M__>Ну собственно то же будет и при runtime_error/
В этом случае я бы составил другой отчёт (где было бы указано, что суть внутренней ошибки заключается в отсутствии обработчика исключения в нужном месте).
M__>Выходит, это чисто формальный вопрос о выборе типа ошибки: runtime или logic, а по сути от этого выбора ничего не зависит.
От этого зависит, насколько легко будет разбираться в исходниках. Конечно, если не принимать это во внимание, то можно хоть "blah_blah_blah_error" бросать.
Здравствуйте, rg45, Вы писали:
S>>аха, а такой вот код, это ошибка выполнения- или таки не совсем?
S>>
S>>rational<int> a(10,0);
S>>
R>А вот тут надо различать два разных момента: первый — это время, когда ошибка была допущена, второй — это когда она обнаруживается. В том месте, где ошибка обнаруживается, об истинных причинах ее возникновения как правило ничего не известно. И исключения как раз и являются средством реакции на ошибки в момент обнаружения. Ошибка, приведенная тобой в качестве примера, бесспорно, была допущена программистом на этапе кодирования, но обнаружится эта ошибка на этапе выполнения.
Но ошибку из этого примера можно обнаружить и до выполнения (при наличии документированных предусловий).
R>Поэтому вполне естественно, что код, обнаруживший эту ошибку сообщит о ней исключением типа runtime_error или производным от него.
Не соглашусь. В большинстве случаев логичнее проводить разделение не по линии "когда допущена"/"когда обнаружена", а по месту исправления (фактически — по расположению дефекта): если возникновение ошибки приводит к необходимости модификации кода — это логическая ошибка. Все остальное — ошибки времени выполнения. В этом случае "исправление" сводится к внешним (по отношению к программе) модификациям.