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