Литмир - Электронная Библиотека
A
A

Исключение

out_of_range
отличается от исключения
runtime_error
, поэтому перехват исключения
runtime_error
не приводит к обработке ошибок
out_of_range
, которые могут возникнуть при неправильном использовании класса
vector
или других контейнерных типов из стандартной библиотеки. Однако и
out_of_range
, и
runtime_error
являются исключениями, поэтому для работы с ними необходимо предусмотреть перехват объекта класса exception.

int main()

try {

  // наша программа

  return 0; // 0 означает успех

}

catch (exception& e) {

  cerr << "error: " << e.what() << '\n';

  keep_window_open();

  return 1; // 1 означает сбой

}

catch (...) {

  cerr << "Ой: неизвестное исключение !\n";

  keep_window_open();

  return 2; // 2 означает сбой

}

Здесь, для того чтобы перехватить все исключения, мы добавили инструкцию

catch(...)
.

Когда исключения обоих типов (

out_of_range
и
runtime_error
) рассматриваются как разновидности одного и того же типа
exception
, говорят, что тип exception является базовым типом (супертипом) для них обоих. Этот исключительно полезный и мощный механизм будет описан в главах 13–16.

Снова обращаем ваше внимание на то, что значение, возвращаемое функцией

main()
, передается системе, вызвавшей программу. Некоторые системы (такие как Unix) часто используют это значения, а другие (такие как Windows), как правило, игнорируют их. Нуль означает, что программа завершилась успешно, а ненулевое значение, возвращенное функцией
main()
, означает какой-то сбой.

При использовании функции

error()
для описания возникшей проблемы часто необходимо передать две порции информации. В данном случае эти две порции просто объединяются в одну строку. Этот прием настолько широко распространен, что мы решили представить его в виде второго варианта функции
error()
.

void error(string s1, string s2)

{

  throw runtime_error(s1+s2);

}

Этой простой обработки ошибки нам будет достаточно, пока ситуация не усложнится и потребуется придумать более изощренный способ исправить ситуацию.

Обратите внимание на то, что использование функции

error()
не зависит от количества ее предыдущих вызовов: функция
error()
всегда находит ближайший раздел
catch
, предусмотренный для перехвата исключения
runtime_error
(обычно один из них размещается в функции
main()
). Примеры использования исключений и функции
error()
приведены в разделах 7.3. и 7.7. Если исключение осталось неперехваченным, то система выдаст сообщение об ошибке (неперехваченное исключение).

ПОПРОБУЙТЕ

Для того чтобы увидеть неперехваченное исключение, запустите небольшую программу, в которой функция

error()
не перехватывает никаких исключений.

5.6.4. Суживающие преобразования

В разделе 3.9.2 продемонстрирована ужасная ошибка: когда мы присвоили переменной слишком большое значение, оно было просто усечено. Рассмотрим пример.

int x = 2.9;

char c = 1066;

 

Программирование. Принципы и практика использования C++ Исправленное издание - _003.png
 Здесь
x
будет равно
2
, а не
2.9
, поскольку переменная
x
имеет тип
int
, а такие числа не могут иметь дробных частей. Аналогично, если используется обычный набор символов ASCII, то переменная
c
будет равна
42
(что соответствует символу
*
), а не
1066
, поскольку переменные типа
char
не могут принимать такие большие значения.

В разделе 3.9.2 показано, как защититься от такого сужения путем проверки. С помощью исключений (и шаблонов; см. раздел 19.3) можно написать функцию, проверяющую и генерирующую исключение

runtime_exception
, если присваивание или инициализация может привести к изменению значения. Рассмотрим пример.

int x1 = narrow_cast<int>(2.9);    // генерирует исключение

int x2 = narrow_cast<int>(2.0);    // OK

char c1 = narrow_cast<char>(1066); // генерирует исключение

char c2 = narrow_cast<char>(85);   // OK

Угловые скобки,

<...>
, означают то же самое, что и в выражении
vector<int>
. Они используются, когда для выражения идеи возникает необходимость указать тип, а не значение. Аргументы, стоящие в угловых скобках, называют шаблонными (template arguments). Если необходимо преобразовать значение и мы не уверены, что оно поместится, то можно использовать тип
narrow_cast
, определенный в заголовочном файле
std_lib_facilities.h
и реализованный с помощью функции
error()
. Слово
cast
[7] означает приведение типа и отражает роль этой операции в ситуации, когда что-то “сломалось” (по аналогии с гипсовой повязкой на сломанной ноге). Обратите внимание на то, что приведение типа не изменяет операнд, а создает новое значение, имеющее тип, указанный в угловых скобках и соответствующий операнду.

5.7. Логические ошибки

После устранения ошибок, выявленных компилятором и редактором связей, программу выполняют. Как правило, после этого программа либо ничего не выдает на печать, либо выводит неправильные результаты. Это может происходить по многим причинам. Возможно, вы просто неправильно поняли логику программы; написали не то, что намеревались; сделали глупую ошибку в какой-нибудь инструкции

if
или что-нибудь еще. Логические ошибки обычно труднее всего находить и исправлять, поскольку на этой стадии компьютер делает только то, что вы сами ему приказали. Теперь ваша задача выяснить, почему он делает не то, что вы хотели. В принципе компьютер — это очень быстро действующий идиот. Он просто покорно делает в точности то, что вы ему сказали.

вернуться

7

Буквально гипсовая повязка. — Примеч. ред.

67
{"b":"847443","o":1}