Освой самостоятельно С++ за 21 день.
Шрифт:
Стек вызовов отслеживает путь выполнения программы. Если функция main вызывает функцию Animal::GetFavoriteFood, а функция GetFavoriteFood — функцию Animal::LookupPreferences, которая, в свою очередь, вызывает функцию fstream::operator>>, то все эти вызовы заносятся в стек вызовов. Рекурсивная функция может оказаться в стеке вызовов много раз.
Исключение передается в стек вызовов для каждого вложенного блока. По мере прохождения стека вызываются деструкторы для локальных объектов, в результате чего эти объекты разрушаются.
За каждым блоком try следует один
Если исключение пройдет весь путь к началу программы (функции main) и все еще не будет перехвачено, вызывается встроенный обработчик, который завершит программу.
Прохождение исключения по стеку можно сравнить с поездкой по улице с односторонним движением. По мере прохождения стека его объекты разрушаются. Назад дороги нет. Если исключение перехвачено и обработано, программа продолжит работу после блока catch, который перехватил это исключение.
Таким образом, в листинге20.1 выполнение программы продолжится со строки 101 — первой строки после блока try catch, перехватившего исключение xBoundary. Помните, что при возникновении исключительной ситуации выполнение программы продолжается после блока catch, а не после того места, где она возникла.
Использование нескольких операторов catch
В некоторых случаях выполнение одного выражения потенциально может быть причиной возникновения нескольких исключительных ситуаций. В этом случае нужно использовать несколько операторов catch, следующих друг за другом, подобно конструкции с оператором switch. При этом эквивалентом оператора default будет выражение catch(.,,), которое следует понимать как "перехватить все". Отслеживание нескольких возможных исключений показано в листинге 20.2.
Листинг 20.2. Множественные исключения
1: #include <iostream.h>
2:
3: const int DefaultSize = 10;
4:
5: class Array
6: {
7: public:
8: // конструкторы
9: Array(int itsSize = DefaultSize);
10: Array(const Array &rhs);
11: ~Array { delete [] pType;}
12:
13: // операторы
14: Array& operator=(const Array&);
15: int& operator[](int offSet);
16: const int& operator[](int offSet) const;
17:
18: // методы доступа
19: int GetitsSize const { return itsSize; }
20:
21: //функция-друг
22: friend ostream& operator<< (ostream&, const Array&);
23:
24: // определение классов исключений
25: class xBoundary { } ;
26: class xTooBig { } ;
27: class xTooSmall { } ;
28: class xZero { } ;
29: class xNegative { } ;
30: private:
31: int *pType;
32: int itsSize;
33: };
34:
35: int& Array::operator[](int offSet)
36: {
37: int size = GetitsSize;
38: if (offSet >= 0J,& offSet < GetitsSize)
39: return pType[offSet];
40: throw xBoundary;
41: return pType[0]; //
требование компилятора42: }
43:
44:
45: const int& Array::operator[](int offSet) const
46: {
47: int mysize = GetitsSize;
48: if (offSet >= 0 && offSet < GetitsSize)
49: return pType[offSet]
50: throw xBoundary;
51:
52: return pType[0]; // требование компилятора
53: }
54:
55:
56: Array::Array(int size):
57: itsSize(size)
58: {
59: if (size == 0)
60: throw xZero;
61: if (size < 10)
62: throw xTooSmall;
63: if (size > 30000)
64: throw xTooBig;
65: if (size < 1)
66: throw xNegative;
67:
68: pType = new int[size];
69: for (int i = 0; i<size: i++)
70: pType[i] = 0;
71: }
72:
73:
74:
75: int main
76: {
77:
78: try
79: {
80: Array intArray(0);
81: for (int j = 0; j< 100; j++)
82: {
83: intArray[j] = ];
84: cout << "intArray[" << j << "] okay...\n";
85: }
86: }
87: catch (Array::xBoundary)
88: {
89: cout << "Unable to process your input!\n";
90: }
91: catch (Array::xTooBig)
92: {
93: cout << "This array is too big...\n";
94: }
95: catch (Array::xTooSmall)
96: {
97: cout << "This array is too small...\n";
98: }
99: catch (Array::xZero)
100: {
101: cout << "You asked for an array";
102: cout << " of zero objects!\n";
103: }
104: catch (... )
105: {
106: cout << "Something went wrong!\n";
107: }
108: cout << "Done.\n";
109: return 0;
110: }
Результат:
You asked for an array of zero objects!
Done
Анализ: В строках 26—29 создается четыре новых класса: xTooBig, xTooSmall, xZero и xNegative. В строках 56—71 проверяется размер массива, переданный конструктору. Если он слишком велик или мал, а также отрицательный или нулевой, генерируется исключение.
За блоком try следует несколько операторов catch для каждой исключительной ситуации, кроме исключения, связанного с передачей отрицательного размера. Данное исключение перехватывается оператором catch(. ..) в строке 104.
Опробуйте эту программу с рядом значений для размера массива. Затем попытайтесь ввести значение -5. Вы могли бы ожидать, что будет вызвано исключение xNegative, но этому помешает порядок проверок, заданный в конструкторе: проверка size < 10 выполняется до проверки size < 1. Чтобы исправить этот недостаток, поменяйте строки 61 и 62 со строками 65 и 66 и перекомпилируйте программу.