Re[5]: Наследование класса std::exception
От: Mak_71_rus Россия  
Дата: 02.07.10 17:12
Оценка:
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.
Разве это не ошибка времени выполнения?
Re[6]: Наследование класса std::exception
От: Sni4ok  
Дата: 02.07.10 19:11
Оценка:
Здравствуйте, Mak_71_rus, Вы писали:
M__>Разве это не ошибка времени выполнения?

вы путаете мягкое со сладким, bad_alloc — это ошибка времени исполнения или нет?
Re[5]: Наследование класса std::exception
От: Masterkent  
Дата: 02.07.10 20:26
Оценка: 7 (2)
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-а может быть вполне достаточно.
Re[7]: Наследование класса std::exception
От: Mak_71_rus Россия  
Дата: 03.07.10 05:26
Оценка:
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;


Тут знаменатель вводит пользователь. И тогда то это точно ошибка времени выполнения. Её невозможно предусмотреть до запуска программы и получения соответствующих значений от пользователя.
Re[2]: Наследование класса std::exception
От: Sergey Chadov Россия  
Дата: 03.07.10 07:24
Оценка: 2 (1)
Здравствуйте, Mak_71_rus, Вы писали:

M__>А такая иерархия ошибок не слишком сложна? Может просто оставить класс переполнения, неверного ввода и нулевого знаменателя (всего три вместо этой кучи)?


А ты можешь представить себе ситуацию, когда эти ошибки нужно обрабатывать отдельно, т.е

try{ some code}
catch(const addition_overflow&){
    действие 1
}
catch(const multiplication_overflow&){
    действие 2
}

?
Если да, то такая иерархия оправдана, иначе нет.
--
Sergey Chadov

... << RSDN@Home 1.2.0 alpha rev. 685>>
Re[8]: Наследование класса std::exception
От: Masterkent  
Дата: 03.07.10 07:29
Оценка:
Mak_71_rus:

M__>Тут знаменатель вводит пользователь. И тогда то это точно ошибка времени выполнения. Её невозможно предусмотреть до запуска программы и получения соответствующих значений от пользователя.


По DbC нарушение предусловий — это всегда программная ошибка. Тот, кто вызывает функцию, обязан следить за тем, чтобы в неё передавались корректные аргументы. Если при правильных аргументах функция не может выполнить свою задачу в силу внешних по отношению к программе факторов, то это уже либо ошибка времени выполнения, либо запланированное прерывание её работы.
Re[9]: Наследование класса std::exception
От: Masterkent  
Дата: 03.07.10 07:45
Оценка:
Впрочем, некоторые сформулированные предусловия могут быть упрощены (во избежание чрезмерной сложности), тогда невозможность выполнения задачи может целиком зависеть от переданных аргументов. Но здесь упрощать предусловия я смысла не вижу ("знаменатель должен быть отличен от нуля" — это очень простое предусловие, которое легко проверить до вызова функции).
Re[10]: Наследование класса std::exception
От: Mak_71_rus Россия  
Дата: 04.07.10 05:04
Оценка:
Здравствуйте, Masterkent, Вы писали:

M>Впрочем, некоторые сформулированные предусловия могут быть упрощены (во избежание чрезмерной сложности), тогда невозможность выполнения задачи может целиком зависеть от переданных аргументов. Но здесь упрощать предусловия я смысла не вижу ("знаменатель должен быть отличен от нуля" — это очень простое предусловие, которое легко проверить до вызова функции).


А зачем вообще использовать контрактное программирование в данном случае, только ради того, чтобы не проверять несколько условий? Зато если его не использовать класс будет использовать проще.
Re[11]: Наследование класса std::exception
От: Masterkent  
Дата: 04.07.10 07:33
Оценка:
Mak_71_rus:

M__>А зачем вообще использовать контрактное программирование в данном случае, только ради того, чтобы не проверять несколько условий?


А что, на твой взгляд, более естественно: проверять ввод на ошибки сразу или же посылать куда-то заведомо некорректные данные и получать отказ?

M__>Зато если его не использовать класс будет использовать проще.


Чем именно проще? Отказ ведь тоже нужно где-то обрабатывать. Причём информация "в функцию передали ноль" (которая может содержаться в твоём исключении) довольно мало о чём говорит (без знания контекста она практически бесполезна).
Re[12]: Наследование класса std::exception
От: Mak_71_rus Россия  
Дата: 04.07.10 18:46
Оценка:
Здравствуйте, Masterkent, Вы писали:

M>А что, на твой взгляд, более естественно: проверять ввод на ошибки сразу или же посылать куда-то заведомо некорректные данные и получать отказ?


Естественнее сразу, но если это не сделано? Теоретически классом пользуется человек, не имеющий никакого отношения к его разработке. Рарабатывая свою программу, он может допустить ошибку и не проверить отправляемые данные на корректность. Если исключения не будет, то программа будет вести себя непредсказуемо: может упадёт, а может просто выдаст неверные данные. Во втором случае это сложно заметить сразу; а если заметить, то сложно будет найти причину такого поведения программы.


M>Чем именно проще? Отказ ведь тоже нужно где-то обрабатывать. Причём информация "в функцию передали ноль" (которая может содержаться в твоём исключении) довольно мало о чём говорит (без знания контекста она практически бесполезна).


Проще тем, что если эта ошибка не будет обработана вовсе, то хоть при отладке можно понять в чём дело (само имя класса ошибки будет выведено в окне сообщения об ошибке). Понятно, что если функция генерирует исключения, то они должны быть перехвачены, но мали как будет использован класс. И сгенерированное исключение лучше, чем просто падение програмы, неведомо от чего.

А информация "в функцию передали ноль" может всего лишь позволить сообщить пользователю программы, что задача не выполнена, т.к. введены ошибочные данные. А больше ничего сделать не получится: если пользователь сказал: "Дели на ноль", можно только ответить: "Это не возможно", а разделить всё равно не получится.
Re: Наследование класса std::exception
От: Mak_71_rus Россия  
Дата: 04.07.10 18:53
Оценка:
Подумав над сказанным здесь переделал иерархию классов так, чтобы они, во-первых, наследовались от базового класса ошибок класса rational, а, во-вторых, от соответствующих типов стандартных ошибок:


//Классы ошибок

//базовый класс для всех ошибок при действиях со смешанными числами
    class rational_exception
    {
    protected:
        unsigned short err_code;
    public:
        unsigned short get_err_code(){return err_code;}
    };

//переполнение, возникшее в результате сложения двух чисел типа unsigned long long
    class 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 long
    class 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


 catch ( rational_exception &e ) 
   {
      cerr << "Перехвачено исключение: " << e.what( ) << endl;
      cerr << "Тип исключения: " << typeid( e ).name( ) << endl;
   };
Re: Наследование класса std::exception
От: los puercos  
Дата: 05.07.10 07:41
Оценка:
Здравствуйте, Mak_71_rus, Вы писали:

M__>Здравствуйте, форумчане!


M__>Подскажите пожалуёста, если я создаю собственный класс ошибок, всегда ли он должен быть производным от std::exception?


M__>Я решил написать класс rational, представляющий смешанное число. В результате сложения может призойти переполнение, или может быть попытка присвоить знаменателю нулевое значение — всё это исключительные ситуации. Я создал простую иерархию классов ошибок, базовый класс содержит только номер ошибки, а все производные — функцию-член, возвращающие этот номер. Мне кажется, такая система удобна в отладке, но стоит ли её оставлять как есть? Может лучше наследовать базовый класс от std::exceptin? Просто все строки, возвращаемые функцией what занимают места намного больше, чем сам экземпляр класса.


Наследовать не обязательно, но ты облегчишь жизнь людям, реюзающим твой код. Обычно после ловли всех конкретных исключений ловятся std::exception, потом — все остальные, через catch(...). Если не хочешь, чтобы твои исключения попали в секцию catch(...), не оставив никакой полезной информации, наследуйся от std::exception (std::runtime_error, std::logic_error, в зависимости от контекста).
Re[12]: Наследование класса std::exception
От: March_rabbit  
Дата: 05.07.10 09:19
Оценка:
Здравствуйте, Masterkent, Вы писали:

M__>>А зачем вообще использовать контрактное программирование в данном случае, только ради того, чтобы не проверять несколько условий?


M>А что, на твой взгляд, более естественно: проверять ввод на ошибки сразу или же посылать куда-то заведомо некорректные данные и получать отказ?

кстати, а как это утверждение соотносится с инкапсуляцией и прочими прелестями? Получается, что нельзя положиться на "черный ящик" из какого-то модуля. Надо четко знать, какие условия необходимы ему для работы и прочее.
Re[13]: Наследование класса std::exception
От: Masterkent  
Дата: 05.07.10 09:27
Оценка:
Mak_71_rus:

M>>А что, на твой взгляд, более естественно: проверять ввод на ошибки сразу или же посылать куда-то заведомо некорректные данные и получать отказ?


M__>Естественнее сразу, но если это не сделано?


Если проверка должна быть сделана, но не сделана, значит, ошибка в самой программе. Реагировать на программные ошибки можно по-разному: можно вставить assert, можно выбросить исключение, производное от std::logic_error, можно вызвать свой обработчик.

M__>Теоретически классом пользуется человек, не имеющий никакого отношения к его разработке.


Предусловия следует документировать.

M__>Рарабатывая свою программу, он может допустить ошибку и не проверить отправляемые данные на корректность. Если исключения не будет, то программа будет вести себя непредсказуемо: может упадёт, а может просто выдаст неверные данные. Во втором случае это сложно заметить сразу; а если заметить, то сложно будет найти причину такого поведения программы.


Лишние проверки могут негативно сказаться на производительности. Лучшего решения (проверять или не проверять аргументы функции на ошибки) на все случаи не существует.

M>>Чем именно проще? Отказ ведь тоже нужно где-то обрабатывать. Причём информация "в функцию передали ноль" (которая может содержаться в твоём исключении) довольно мало о чём говорит (без знания контекста она практически бесполезна).


M__>Проще тем, что если эта ошибка не будет обработана вовсе, то хоть при отладке можно понять в чём дело


При отладке можно полагаться и на assert-ы. Это вполне стандартное средство отлова программных ошибок.
Re[13]: Наследование класса std::exception
От: Masterkent  
Дата: 05.07.10 09:35
Оценка:
March_rabbit, Вы писали:

M__>>>А зачем вообще использовать контрактное программирование в данном случае, только ради того, чтобы не проверять несколько условий?


M>>А что, на твой взгляд, более естественно: проверять ввод на ошибки сразу или же посылать куда-то заведомо некорректные данные и получать отказ?

M_>кстати, а как это утверждение

Какое именно утверждение? В процитированном я вижу только вопросы.

M_>соотносится с инкапсуляцией и прочими прелестями?


А что не так с инкапсуляцией? И что за прочие прелести?

M_>Получается, что нельзя положиться на "черный ящик" из какого-то модуля.


Почему нельзя?

M_>Надо четко знать, какие условия необходимы ему для работы


И что в этом плохого?
Re[14]: Наследование класса std::exception
От: Mak_71_rus Россия  
Дата: 05.07.10 14:28
Оценка:
M>Если проверка должна быть сделана, но не сделана, значит, ошибка в самой программе. Реагировать на программные ошибки можно по-разному: можно вставить assert, можно выбросить исключение, производное от std::logic_error, можно вызвать свой обработчик.

А какая собственно разница, какое исключение поднимать logic_error или runtime_error? Всё равно перехват с помощбю catch

M>Предусловия следует документировать.

Это само собой.

M>Лишние проверки могут негативно сказаться на производительности. Лучшего решения (проверять или не проверять аргументы функции на ошибки) на все случаи не существует.

Тут придётся выбрать между увеличением производительности и (в моём понимании) удобством использования. Я буду придерживаться концепции: программа должна работать должным образом или сообщить об ошибке. Ни при каких входных данных невозможно получить неверный результат.

M>>>Чем именно проще? Отказ ведь тоже нужно где-то обрабатывать. Причём информация "в функцию передали ноль" (которая может содержаться в твоём исключении) довольно мало о чём говорит (без знания контекста она практически бесполезна).


M__>>Проще тем, что если эта ошибка не будет обработана вовсе, то хоть при отладке можно понять в чём дело

M>При отладке можно полагаться и на assert-ы. Это вполне стандартное средство отлова программных ошибок.

Код класса будет уже скомпилирован к моменту отладки кода, использующего класс.
Re[15]: Наследование класса std::exception
От: Masterkent  
Дата: 05.07.10 23:59
Оценка:
Mak_71_rus:

M>>Если проверка должна быть сделана, но не сделана, значит, ошибка в самой программе. Реагировать на программные ошибки можно по-разному: можно вставить assert, можно выбросить исключение, производное от std::logic_error, можно вызвать свой обработчик.


M__>А какая собственно разница, какое исключение поднимать logic_error или runtime_error?


Ну, если программные и все остальные ошибки ты обрабатываешь одинаково, то для тебя никакой разницы не будет.
Re[16]: Наследование класса std::exception
От: Mak_71_rus Россия  
Дата: 06.07.10 06:20
Оценка:
M__>>А какая собственно разница, какое исключение поднимать logic_error или runtime_error?

M>Ну, если программные и все остальные ошибки ты обрабатываешь одинаково, то для тебя никакой разницы не будет.


А как надо обрабатывать logic_error? Вот в знаменатель передан 0. Выбрасывается исключение logic_error. И?
Re[3]: Наследование класса std::exception
От: Pasternak  
Дата: 06.07.10 14:58
Оценка:
Здравствуйте, kpcb, Вы писали:

K>По-моему проще сделать один класс, а конкретный тип исключения передавать как константу в конструктор. Проще в одном catch-e ловить исключение и проверять тип, чем писать туеву хучу catch-ей.


...

K>
K>try {
K>    //....
K>} catch(rational_exception const& ex) {
K>    switch(ex.code) {
K>        case kErrorAdditionOverflow: //....
K>        case kErrorMultiplicationOwerflow: //....
K>        //....
K>    }
K>} catch(std::exception const &ex) {
K>    /* TODO: */
K>} catch(...) {
K>    /* TODO */
K>}
K>


А чем туева хуча captch-ей лучше туевой хучи проверок типа ошибки? И потом, к исключению иногда полезно еще какую-то информацию прикрепить, связаную с ошибкой. Неужели всё в один клас пихать?
Re[17]: Наследование класса std::exception
От: Masterkent  
Дата: 06.07.10 16:07
Оценка:
Mak_71_rus:

M__>А как надо обрабатывать logic_error?


Это зависит от того какие требования предъявляются к программе в случае обнаружения в ней внутренних ошибок.

M__>Вот в знаменатель передан 0. Выбрасывается исключение logic_error. И?


Например, можно записать отчёт об ошибке в лог-файл, сообщить пользователю об обнаружении внутренней ошибки программы и о том, что программа будет закрыта, а потом убить процесс.
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.