Здравствуйте, Ashen_Angel, Вы писали:
A_A>Здравствуйте, Blitz, Вы писали:
B>>что генерирует throw??? что ловит catch??? что вообще catch делает??? почему не проще написать if(случилось самое страшное){ испугаться и зависнуть}???
A_A>Попробуй почитать асмовский листинг, там всё прозрачно, вроде по throw генерируется jmp, A_A>а catch — всего-лишь метка, куда надо прыгнуть. Подробности не помню, давно разбирался.
Ничего личного, но такие вот ответы — иллюстрация того,
что знание ассемблера может быть вредным
Здравствуйте, Аноним, Вы писали:
A_A>>Попробуй почитать асмовский листинг, там всё прозрачно, вроде по throw генерируется jmp, A_A>>а catch — всего-лишь метка, куда надо прыгнуть. Подробности не помню, давно разбирался.
А>Ничего личного, но такие вот ответы — иллюстрация того, А>что знание ассемблера может быть вредным
Никакого знания здесь нет, throw неявно вызывает функцию _CxxThrowException которая вызывает RaiseException
Где там JMP
Здравствуйте, adontz, Вы писали:
А>>Ничего личного, но такие вот ответы — иллюстрация того, А>>что знание ассемблера может быть вредным
A>Никакого знания здесь нет, throw неявно вызывает функцию _CxxThrowException которая вызывает RaiseException A>Где там JMP
Здравствуйте, adontz, Вы писали:
A>Никакого знания здесь нет, throw неявно вызывает функцию _CxxThrowException которая вызывает RaiseException A>Где там JMP
Здравствуйте, Аноним, Вы писали:
А>Ничего личного, но такие вот ответы — иллюстрация того, А>что знание ассемблера может быть вредным
Знание не может быть вредным.
Читай соседний пост.
Здравствуйте, Willi, Вы писали:
W>Однако, я не понимаю к чему этот вопрос. W>Я не собираюсь меряться с тобой "поинтерами"
Сорри, неправильно написал. Следует читать:
Думаешь нельзя реализовать это через обычные jump'ы?
А то как наезд выглядит
Здравствуйте, adontz, Вы писали:
A>Никакого знания здесь нет, throw неявно вызывает функцию _CxxThrowException которая вызывает RaiseException A>Где там JMP
Упс, я не о том болтаю Писал как-то трейсер для стека, так
там понадобились __try/__except, щас глянул на try/throw/catch — всё
так как ты сказал. В случае с __try/__except всё примерно так, как
описал я (ну только там не jmp ес-но).
Прошу прощения за флейм
B>Вопрос у меня довольно пространный, не обессудьте. Но я никак не могу понять как работает exception handling... B>Все эти try — catch... больше всего пугает throw... B>НЕ ВЪЕЗЖАЮ!!!! Не могли бы вы объяснить как-нибудь как пользоваться try-catch???
B>что генерирует throw??? что ловит catch??? что вообще catch делает??? почему не проще написать if(случилось самое страшное){ испугаться и зависнуть}???
Читай сюда! Это первая часть. Вторая — в соседнем сообщении
Кстати и сообщи, как читается.
Глава 4. Исключения
Не бывает правил без исключений
Народный фольклор
При выполнении любой программы бывают аварийные ситуации, вызванные самыми разнообразными причинами. Если мы делаем программу "для себя", то встроить в нее обработку некоторых ошибок с выдачей сообщений труда не представляет. Но в профессиональном программировании программы чаще пишутся не для себя, а для других, поэтому такой метод обработки ошибок неприемлем.
Принципы обработки исключений
В С++ исключение — это объект. Хотя обычно говорят об исключительной ситуации в программе, такая точка зрения мало что дает, так как с ситуацией сделать ничего нельзя. Поэтому в С++ при возникновении исключительной ситуации программа должна генерировать объект-исключение. Собственно, сама генерация объекта-исключения и создает исключительную ситуацию. Такой подход очень удобен, так как с объектами, в отличие от ситуации, мы можем много чего делать. Например, объявлять как обычную переменную, передать его как параметр любым из возможных способов или возвратить в качестве результата. Можно объявлять массивы исключений или включать объекты исключения в качестве полей в другие классы. В дальнейшем мы иногда будем использовать термин "исключение", понимая под этим объект-исключение.
Общая схема обработки исключений такова: в одной части программы, где обнаружена аварийная ситуация, исключение генерируется; другая часть программы, которая следит за возникновением исключения, ловит и обрабатывает его. В С++ есть три зарезервированных слова: try (контролировать), catch (ловить), throw (порождать), — которые и используются для организации процесса обработки исключений.
Генерация исключений
Генерируется исключение оператором throw, который имеет следующий синтаксис
throw выражение_генерации_исключения;
Фраза "выражение_генерации_исключения" на практике означает либо константу, либо переменную некоторого типа. Тип объекта-исключения может быть любым, как встроенным, так и определяемым программистом. Например,
throw 7;
throw"Ошибка: деление на ноль!";
throw Message[i];
throw M_PI;
В первом случае объект-исключение — это целая константа, которая может быть условным номером-кодом ошибки. В общем случае этот код ошибки может вычисляться, например
throw 3*v-i;
Выражение, конечно, может быть не только целочисленным.
Второй вариант — это символьная константа, которая фактически является сообщением об ошибке. Если все сообщения об ошибках записаны в массиве, например,
string Message[326];
то в третьем варианте объект-исключение тоже представляет собой строку — сообщение об ошибке. В последнем примере в качестве объекта используется дробная константа — число p.
Программист может и сам определить свой собственный тип объекта-исключения, объявив новый класс, например
class NegativeArgument{};
NegativeArgument exeption;
if (x>0) double t = x/sqrt(x);
else throw exeption;
Пустые классы неожиданно пригодились! Однако объявлять переменную совсем не обязательно, можно обойтись и без этого, например
throw NegativeArgument();
Здесь в качестве "выражения генерации исключения" мы использовали явный вызов конструктора без аргументов, и это наиболее часто используемая форма генерации исключения.
Объект-исключение может быть и динамическим, например,
throw new NegativeArgument();
Соответственно в программе должен быть объявлена секция-ловушка, в которой параметр передается по указателю. Однако такая практика приводит к проблемам с возвратом памяти, поэтому лучше не создавать себе головной боли и не использовать указатели в качестве параметров catch-блоков.
Перехват и обработка исключений
Сгенерировать исключение — это только полдела. Исключение надо перехватить и обработать. Проверка возникновения исключения делается с помощью оператора try, с которым неразрывно связаны одна или несколько секций-ловушек catch. Оператор try объявляет в любом месте программы контролируемый блок, который имеет следующий вид
try { /* контролируемый блок */ }
Контролируемый блок, помимо функции контроля, обладает функциями обычного блока: все переменные, объявленные внутри него, являются локальными в этом блоке и не видны вне его.
После try-блока обязательно прописывается один или несколько catch-блоков, которые обычно называют обработчиками исключений. Форма записи секции-ловушки следующая:
catch (спецификация_исключения) { /* блок обработки */}
Спецификация_исключения может иметь следующие три формы:
(тип имя)
(тип)
(…)
Тип должен быть одним из допустимых типов исключений — либо встроенный, либо определенный программистом. Первый вариант спецификации означает, что объект-исключение передается в блок обработки, чтобы там его как-то использовать, например, для вывода информации в сообщении об ошибке. При этом объект-исключение может передаваться в секцию-ловушку любым способом: по значению, по ссылке или по указателю, например
catch (TException e) // по значениюcatch (TException& e) // по ссылкеcatch (const TException& e) // по константной ссылкеcatch (TException *e) // по по указателю
Однако в отличие от параметров функций, никаких преобразований по умолчанию не производится. Это означает, что если в программе написан заголовок обработчика,
catch (double e) // по значению
то попадают в эту секцию только те исключения, тип выражения исключений которых совпадает с double. Оператор
throw 1
генерирует исключение целого типа, поэтому будет обрабатываться секцией-ловушкой с заголовком, в которой прописан целый тип исключения, например
catch (int e) // по значению
или
catch (int) // без передачи информации в секцию-ловушку
Во втором варианте спецификации исключения объект-исключение в блок обработки не передается и никак не используется — исключения перехватываются по типу.
Первые две формы предназначены для обработки конкретного типа исключений. Если же на месте спецификации исключения написано многоточие (как в функциях с переменным числом параметров), то такой обработчик перехватывает все исключения.
Работа конструкции try-catch напоминает работу оператора switch. Секции-ловушки похожи на альтернативы case, а catch-блок с многоточием соответствует альтернативе default оператора-переключателя. Если в try-блоке возникает исключение, то начинается сравнение типа сгенерированного исключения и типов параметров в секциях-ловушках. Выполняется тот catch-блок, тип параметра которого совпал с типом исключения. Если такого типа не найдено, но есть catch с многоточием, то выполняется его блок. Если же такого блока нет, то вызывается стандартная функция завершения terminate().
Таким образом, очень важен порядок записи секций-ловушек после контролируемого блока. Если в качестве первого обработчика после try-блока задан catch-блок с параметром-многоточием, то такой обработчик будет обрабатывать все возникающие исключения. До остальных секций-ловушек дело просто не дойдет. Поэтому усвойте следующее простое правило: всегда задавать catch-блок с параметром-многоточием последним.
Выход из секции-ловушки выполняется одним из следующих способов:
1. выполнились все операторы обработчика — происходит неявный переход на оператор, расположенный после конструкции try-catch;
2. в обработчике выполняется оператор goto; разрешается выполнять оператор на любой оператор вне конструкции try-catch; внутрь контролируемого блока и в другую секцию ловушку переход запрещен;
3. в секции ловушке выполняется оператор throw;
4. в обработчике генерируется другое исключение;
После выполнения операторов catch-блока при отсутствии явных операторов перехода или оператора throw выполняются операторы, расположенные после всей конструкции try-catch. Если во время работы в try-блоке не обнаружено исключительной ситуации, то все catch-блоки пропускаются, и программа продолжает выполнение с первого оператора после всех catch.
Интересно, что можно объявлять контролируемым блок, являющийся телом функции. Например, мы можем контролировать все исключения, возникающие в теле главной функции, написав такую конструкцию:
int main()
try { // контролируемый блок – тело функции
// …
}
catch(...)
{ // сообщения об исключениях
}
Если в контролируемом блоке не возникнет исключений, то блок функции нормально завершается. Если же исключения возникнут, то, как обычно, выполняется секция-ловушка, и только после этого программа завершается. Секций-ловушек, естественно, может быть прописано столько, сколько необходимо.
В стандарте [1] приводится следующий пример на ту же тему (листинг 4.1).
//Листинг 4.1. Контролируемый блок — тело функцииint f(int); // функция, определенная в другом местеclass C
{ int i; double d; // поляpublic:
C(int, double); // конструктор инициализации
// …
};
C::C(int ii, double id) // реализация конструктора try// контролируем
:i(f(ii)), d(id) // список инициализации
{ // … // тело конструктора
}
catch(…)
{ // … // обработчик исключений
}
В этом случае у нас отсутствуют операторы после блока try-catch. При такой организации конструктора первые два из перечисленных выше способов выхода из секции-ловушки просто невозможны. Поэтому в данном случае в catch-блоке конструктора обязательно должен присутствовать оператор throw. Однако нужно сказать, что не все системы поддерживают стандарт в полном объеме, поэтому такие возможности следует проверять в конкретной системе.
Блоки try-catch могут быть вложенными, причем как в try-блок, так и в catch-блок:
try { // блок, который может инициировать исключенияtry { //вложенный блок
}
catch(…){ }
}
catch (исключение) { // обработка исключенияtry { //вложенный блок
}
catch(…){ }
}
Необходимо отметить, что исключение может быть сгенерировано в одном месте программы, а обработано совершенно в другом.
При обработке исключения в секции-ловушке можно сгенерировать повторное исключение того же типа — это делается оператором throw без выражения генерации исключения. Такая форма допустима только внутри секции-ловушки. Однако это не приводит к рекурсивному входу в тот же обработчик — ищется другой обработчик выше по иерархии вложенности. Если такого обработчика нет, то программа аварийно завершается. Таким образом, мы имеем возможность "распределить" обработку исключения по разным частям программы.
Хочешь быть счастливым — будь им!
Без булдырабыз!!!
B>Вопрос у меня довольно пространный, не обессудьте. Но я никак не могу понять как работает exception handling... B>Все эти try — catch... больше всего пугает throw... B>НЕ ВЪЕЗЖАЮ!!!! Не могли бы вы объяснить как-нибудь как пользоваться try-catch??? B>что генерирует throw??? что ловит catch??? что вообще catch делает??? почему не проще написать if(случилось самое страшное){ испугаться и зависнуть}???
Другая часть.
Текст функции с программой-клиентом представлен в листинге 4.3.
//Листинг 4.3 Функция вычисления высоты треугольника с генерацией исключенийdouble Ha(double a, double b, double c)
{ if ((a>0)&&(b>0)&&(c>0)) // если параметры правильные
{ if ((a+b>c)&&(a+c>b)&&(b+c>a)) // если треугольник
{ double p = (a+b+c)/2; // вычисление высотыreturn 2*sqrt(p*(p-a)*(p-b)*(p-c))/a;
}
else throw"Не треугольник!"; // генерируем исключение
}
else throw"Неправильный параметр!"; // генерация исключения
}
int main()
{ try { // контролируемый блок
cout <<Ha(3,4,5)<< endl; // нормальное вычисление
cout <<Ha(1,2,3)<< endl; // генерирует исключение
cout <<Ha(1,0,3)<< endl; // не выполняется!
}
catch(const char *s) // обработка исключения
{ cout << s << endl; }
return 0;
}
Обратите внимание на то, что в функции не выполняется возврат "аварийного значения" 0 — в данном случае этого не требуется, так как оператор генерации исключения осуществляет выход из функции. При этом выполняется обычный процесс уничтожения локальных объектов, и вызываются все необходимые деструкторы. Этот процесс уничтожения локальных объектов при выходе по исключению называется "раскруткой" стека. Поэтому вернуться в функцию после генерации исключения невозможно — разве что заново ее вызвать.
Первый оператор вывода срабатывает нормально. Во время выполнения второго возникает исключение, и управление передается в секцию-ловушку. При этом пропускается третий оператор вывода. И мы не можем "вернуться" к его выполнению после выполнения секции-ловушки, даже если пометим его, и напишем оператор goto, — по стандарту оператор goto не может использоваться для перехода внутрь контролируемого блока. Но можно передать управление на начало try-блока и выполнить весь контролируемый блок сначала. Правда, в данном случае — без корректировки параметров во втором вызове — это не изменит поведения программы: третий оператор все равно не будет работать.
Наша функция не имеет спецификации исключений. Наличие спецификации в заголовке, например,
double Ha(double a, double b, double c) throw(const char *)
никак не изменяет поведения программы.
Если мы напишем спецификацию исключения с типом исключения, не соответствующим типу генерируемого, то программа должна закончится аварийно. Однако не все системы поддерживают это требование стандарта. В частности, Visual C++.NET 2003 задание спецификация с другим типом исключения, например,
double Ha(double a, double b, double c) throw(string)
или вовсе запрещающая исключения
double Ha(double a, double b, double c) throw()
никак не влияет на видимое поведение программы. Главное, чтобы генерируемое исключение было где-нибудь перехвачено.
Передача информации в блок обработки
При обработке исключения в нашей программе просто выводится сообщение. Однако чрезвычайно полезно иметь дополнительную информацию об ошибке. Например, бывает необходимо знать конкретные значения параметров функции, которые вызвали сбой программы. Однако параметр в секцию ловушку передается только один — объект-исключение. Поэтому, чтобы передать в блок обработки больше информации, мы должны реализовать собственный тип исключений в виде полноценного класса. Этот класс, естественно, должен быть специализирован под задачу.
В нашем случае, кроме сообщения, полезно передать в блок обработки вместе с объектом-исключением значения параметров, которые получила функция (стороны треугольника) и которые вызвали генерацию исключения. Для этого надо в классе-исключении объявить поля и определить конструктор инициализации. В листинге 4.4 представлен текст этого класса-исключения вместе с функцией и тестовой программой.
//Листинг 4.4 Класс-исключение. Передача информации в обработчик struct ErrorTriangle
{ double a, b, c; // параметры функции
string message; // сообщение
// конструктор
ErrorTriangle(double x, double y, double z, const string& s)
: a(x), b(y), c(z),
message(s) {} // пустое тело
}
// указана спецификация исключенийdouble Ha(double a, double b, double c) throw(ErrorTriangle)
{ if ((a>0)&&(b>0)&&(c>0))
{ if ((a+b>c)&&(a+c>b)&&(b+c>a))
{ double p = (a+b+c)/2;
return 2*sqrt(p*(p-a)*(p-b)*(p-c))/a;
}
else throw ErrorTriangle(a,b,c,"Не треугольник!"); // конструктор
}
throw ErrorTriangle(a,b,c,"Неправильные параметры!"); // конструктор
}
int main()
{ try {
cout <<Ha(3,4,5)<< endl; // нормально выполняется
cout <<Ha(1,2,3)<< endl; // вызывает исключение
cout <<Ha(1,0,3)<< endl; // не выполняется
}
catch(const ErrorTriangle& e) // получаем полноценный объект
{ cout << e.message << endl; // используем поля
cout << e.a <<' '<< e.b <<' '<< e.c << endl;
}
}
Класс-исключение определен с помощью структуры для того, чтобы в обработчике не иметь проблем с доступом к полям. Три поля double соответствуют сторонам треугольника, а еще одно поле — для сообщения об ошибке. Теперь мы можем использовать для сообщений тип string, так как в структуре объявлен конструктор инициализации, который и заполняет поля. При передаче в конструктор символьной константы как параметра и выполняется преобразование в string.
В операторе throw в качестве выражения генерации исключения прописан явный вызов конструктора. Секция-ловушка получает объект по константной ссылке и использует информацию, предоставленную конструктором для вывода значений на экран.
Могу еще написать, если требуется.
Хочешь быть счастливым — будь им!
Без булдырабыз!!!
B>Вопрос у меня довольно пространный, не обессудьте. Но я никак не могу понять как работает exception handling... B>Все эти try — catch... больше всего пугает throw... B>Мне легче дополнительный if(blah-blah-blah){ kill them all } написать, нежели даже пытаться писать с try — catch.. B>что генерирует throw??? что ловит catch??? что вообще catch делает??? почему не проще написать if(случилось самое страшное){ испугаться и зависнуть}???
Третья часть
Напишем несколько функций, последовательно вызывающих друг друга. В каждой функции создадим "умный" массив с помощью конструктора копирования (чтобы конструктор не вызывался при передаче параметров, их будем передавать по константной ссылке). В самой "внутренней" функции сгенерируем исключение. И наконец, в главной программе будем ловить и обрабатывать возникающие исключения (листинг 4.9).
//Листинг 4.9. Проверка "раскрутки" стека
TArray f2 (const TArray &t) // самая "внутренняя" функция
{ cout << "function f2" << endl; // отслеживаем вход
TArray r(t); // конструируем массив
cout << r << endl; // выполняетсяthrow TArray::bad_Index(); // генерация исключенияreturn r; // не выполняется!!!!!
}
TArray f1 (const TArray &t)
{ cout << "function f1" << endl; // отслеживаем вход
TArray R[] = { t, t, t }; // создается три массива
cout << R[0] << endl; // выполняется
cout << R[1] << endl; // выполняется
cout << R[2] << endl; // выполняется
TArray r = f2(t); // r не создается – не "успевает"return r; // не выполняется!!!!!
}
TArray f(const TArray& a)
{ cout << "function f" << endl; // отслеживаем вход
TArray t = f1(a); // t не создается – не "успевает"
cout << t << endl; // не выполняется!!!!return t; // не выполняется!!!!
}
int main()
{ double a[5] = {1,2,3,4,5};
TArray B(a, a+(sizeof(a)/sizeof(double)));
try { f(B); // контролируем
}
// секции-ловушкиcatch(TArray::bad_Index) {cout <<"Индекс плохой!"<< endl;}
catch(TArray::bad_Range) {cout <<"Диапазон плохой!"<< endl;}
catch(TArray::bad_Size) {cout <<"Размер плохой!"<< endl;}
// непредвиденное исключение catch(...){cout <<"Exceptions!"<< endl;}
return 0;
}
При запуске программы мы увидим на экране следующий вывод:
function f
function f1
Конструктор-копирования!
Конструктор-копирования!
Конструктор-копирования!
1 2 3 4 5
1 2 3 4 5
1 2 3 4 5
function f2
Конструктор-копирования!
1 2 3 4 5
Destuctor
Destuctor
Destuctor
Destuctor
Индекс плохой!
[/ccode]
Последовательность действий в этом примере следующая:
q в главной функции объявляется массив a и из него конструируется "умный" массив B;
q выполняется вход в контролируемый блок и вызов функции f, которой передается параметр — "умный" массив B;
q в функции первым выполняется оператор вывода и на экране появляется первая строка "function f";
q во второй строке функции f прописано объявление "умного" массива t, однако до вызова конструктора дело не доходит, так как вызывается функция f1;
q первый оператор функции f1 выводит на экран строку "function f1";
q далее в функции f1 создается массив R из трех "умных массивов, и конструктор копирования вызывается трижды;
q выполняется вывод на экран трех элементов-массивов;
q далее прописано объявление "умного" массива r, однако вызова конструктора не происходит, так как вызывается функция f2;
q функция f2 выводит на экран строку "function f2";
q создается локальный "умный" массив r, конструктор копирования отрабатывает один раз;
q содержимое массива r выводится на экран;
q генерируется исключение bad_Index;
В этот момент у нас создан один локальный массив в функции f2 и массив R из трех массивов в функции f1, которые находятся в стеке. При генерации исключения деструктор вызывается четыре раза, что мы и наблюдаем на экране. После этого происходит переход в секцию-ловушку и выводится строка "Плохой индекс". Затем выполняется выход из секции в тело главной функции, и программа завершает работу.
Теперь распределим обработку исключения по разным функциям. Добавим в функцию f1 контролирующий блок, и идентифицируем вывод в секции-ловушке в главной программе (листинг 4.10).
[ccode]
//Листинг 4.10. Распределенная обработка исключения
TArray f1 (const TArray &t)
{ cout << "function f1" << endl;
TArray R[] = { t, t, t };
cout << R[0] << endl;
cout << R[1] << endl;
cout << R[2] << endl;
try { // контролируем вызов f2
TArray r = f2(t); // локальный объект
return r; // норамальное завершение
}
catch(TArray::bad_Index)
{ cout <<"f1. Плохой индекс!"<< endl;
throw; // повторная генерация исключения
};
}
int main()
{ double a[5] = {1,2,3,4,5};
TArray B(a, a+(sizeof(a)/sizeof(double)));
try { f(B); // контролируем
}
// секции-ловушки
catch(TArray::bad_Index) {cout <<"Main. Индекс плохой!"<< endl;}
catch(TArray::bad_Range) {cout <<"Диапазон плохой!"<< endl;}
catch(TArray::bad_Size) {cout <<"Размер плохой!"<< endl;}
// непредвиденное исключение
catch(...){cout <<"Exceptions!"<< endl;}
return 0;
}
[/ccode]
Остальные функции оставим без изменения. При запуске этой программы на экране появится следующее:
[code]
function f
function f1
Конструктор копирования!
Конструктор копирования!
Конструктор копирования!
1 2 3 4 5
1 2 3 4 5
1 2 3 4 5
function f2
Конструктор копирования!
1 2 3 4 5 // до этого момента поведение повторяется
Destuctor // при выходе из f2
f1. Плохой индекс! // секция-ловушка в функции f1
Destuctor // выход из f1
Destuctor
Destuctor
Main. Плохой индекс! // секция-ловушка в main
Поведение программы повторяется до генерации исключения. Выполнение оператора throw приводит к выходу из функции f2, при этом вызывается деструктор. В вызывающей функции обнаруживается секция-ловушка нужного типа, которая и выполняется. В конце ее срабатывает оператор повторной генерации исключения. Происходит выход из функции f1 — при этом трижды вызываются деструктор для уничтожения элементов массива R. В вызывающей функции — а это уже главная функция main, ищется секция-ловушка нужного типа. Она у нас в программе есть, и мы наблюдаем ее работу в последней строке, выведенной на экран.
В связи с тем, что деструктор вызывается для уничтожения объектов при генерации любого исключения, сам деструктор генерировать исключения не должен. Как указывает Герб Саттер [24], "все деструкторы должны разрабатываться так, как если бы они имели спецификацию исключения throw(), то есть ни одному исключению не должно быть позволено выйти за пределы деструктора". Если же это произойдет, то программа завершается аварийно.
Хочешь быть счастливым — будь им!
Без булдырабыз!!!
Здравствуйте, Blitz, Вы писали:
B>Вопрос у меня довольно пространный, не обессудьте. Но я никак не могу понять как работает exception handling...
B>Все эти try — catch... больше всего пугает throw... B>НЕ ВЪЕЗЖАЮ!!!! Не могли бы вы объяснить как-нибудь как пользоваться try-catch??? B>что генерирует throw??? что ловит catch??? что вообще catch делает??? почему не проще написать if(случилось самое страшное){ испугаться и зависнуть}???
И последнее
//Листинг 4.11. Иерархия стандартных исключенийclass exception {//… };class logic_error : public exception {//… };class domain_error : public logic_error {//… };class invalid_argument : public logic_error {//… };class length_error : public logic_error {//… };class out_of_range : public logic_error {//… };class runtime_error : public exception {//… };class range_error : public runtime_error {//… };class overflow_error : public runtime_error {
class underflow_error : public runtime_error {//… };class bad_cast : public exception {//… };class bad_alloc : public exception {//… };class bad_tipeid : public exception {//… };class bad_exception : public exception {//… };class ios_base::failure : public exception {//… };
Эта иерархия служит основой для создания собственных исключений и иерархий исключений. Мы можем определять свои собственные исключения, унаследовав их от класса exception. Для работы со стандартными исключениями в программе надо прописать оператор
#include <stdexcept>
Класс exception определен в стандартной библиотеке следующим образом (листинг 4.12).
Что означает слово virtual, мы разберемся во второй части книги, а сейчас обратите внимание на то, что все конструкторы и методы имеют спецификацию, запрещающую генерацию исключений. Это и понятно — если механизм исключений сам будет генерировать исключения, хорошего ждать не приходится — программа закончится аварийно.
Как видим, стандартные исключения включают функцию-метод what(). Использование стандартных исключений продемонстрируем с помощью немного модифицированного примера из справочника интегрированной среды Borland C++ Builder 6 (листинг 4.13).
Получили исключение: basic_string
Получили исключение: a runtime error
Первая строка — это результат работы первого обработчика при неправильном использовании метода replace строки s, вторая – это уже результат явной генерации исключения типа runtime_error в функции f. Обратите внимание на одну замечательную особенность catch-блоков: в качестве типа формального параметра используется базовый класс, тогда как при выполнении в обработчик попадает объект-исключение совсем другого типа — наследника от базового. Это очень важное свойство наследования мы рассмотрим в следующей главе.
Хочешь быть счастливым — будь им!
Без булдырабыз!!!
В деталях не разбирался, но в глаза бросается отсутствие советов,
когда стоит выбрасывать исключение и что такое "исключительная ситуация" вообще.
Говорить, что исключение — это объект я бы не стал.
В общем стоит добавить советы, когда же настал момент
(исключительная ситуация) для throw...
Если я это просмотрел, то извиняюсь
Этот момент в общем-то не является специфичным для С++, но очень важен.
Здравствуйте, bkat, Вы писали:
B>Немного критики...
B>а) В деталях не разбирался, но в глаза бросается отсутствие советов, B>когда стоит выбрасывать исключение и что такое "исключительная ситуация" вообще. B>б)Говорить, что исключение — это объект я бы не стал. B>В общем стоит добавить советы, когда же настал момент B>(исключительная ситуация) для throw... B>Если я это просмотрел, то извиняюсь
B>Этот момент в общем-то не является специфичным для С++, но очень важен.
B>А в целом написано интересно
Всем сразу:
1. книжку пишу
2. отбивка исходников — это прям из шаблона издательства Питер — я только наши теги вставил.
3. Спасибо за критику — она мне очень нужна.
а) ну так я еще 3 поста тут же написал — там продолжение. Хотя советов пока действительно нет. Эта глава — ликбез по исключениям, так как они ИМХО плохо описываются в доступных книжках.
б) Именно объект! Его же можно определить в виде класса со всеми вытекающими. Возможно вы имели ввиду, что встроенные — это не совсем объекты, так сказать?
в) советы будут, наверное в последующих главах — при разработкке конкретной довольно большой программы.
Еще раз спасибо!
Прошу и дальше
Хочешь быть счастливым — будь им!
Без булдырабыз!!!
Здравствуйте, Blitz, Вы писали:
B>Мне легче дополнительный if(blah-blah-blah){ kill them all } написать, нежели даже пытаться писать с try — catch..
Проблема в том, что такой код легко превращается в абсолютно нечитабельный код типа
int status;
status = f1();
if (status != OK)
{
report_status(status);
kill them all;
}
status = f2();
if (status != OK)
{
report_status(status);
kill them all;
}
status = f3();
if (status != OK)
{
report_status(status);
kill them all;
}
status = f4();
if (status != OK)
{
report_status(status);
kill them all;
}
status = f5();
if (status != OK)
{
report_status(status);
kill them all;
}
или в
int status;
status = f1();
if (status != OK) goto err;
status = f2();
if (status != OK) goto err;
status = f3();
if (status != OK) goto err;
status = f4();
if (status != OK) goto err;
status = f5();
if (status != OK) goto err;
goto all_ok;
err:
{
report_status(status);
kill them all;
}
all_ok:
или в
int status;
bool all_ok = false;
status = f1();
if (status == OK)
{
status = f2();
if (status == OK)
{
status = f3();
if (status == OK)
{
status = f4();
if (status == OK)
{
status = f5();
if (status == OK)
all_ok = true;
}
}
}
}
if !(all_ok)
{
report_status(status);
kill them all;
}
какой вариант понятнее, проще и читабельнее?
А ведь исключением может быть не только захудалый int, а любой класс, который может нести в себе кучу полежной информации, а не только статус.
Здравствуйте, adontz, Вы писали:
A>Никакого знания здесь нет, throw неявно вызывает функцию _CxxThrowException которая вызывает RaiseException A>Где там JMP