Обработка ошибок в конструкторах и деструкторах
Несмотря на невозможность конструктора возвращать значение, вы можете из него сгенерировать исключение.
С помощью исключения нетрудно уведомить клиента о результате выполнения конструктора: успешном или нет.
Здесь, однако, есть одна существенная проблема: если исключение "покидает" пределы конструктора, то деструктор для созданного объекта никогда не будет вызван.
Рассмотрим пример конструктора класса, модифицированного с использованием обработки исключения:
GameBoard::GameBoard(int inWidth, int inHeight) throw (bad_alloc): mWidth(inWidth), mHeight(inHeight) {
int i, j;
mCells = new GamePiece* [mWidth];
try {
for (i = 0; i <mWidth; i++) {
mCells[i] = new GamePiece[mHeight];
}
} catch (...) {
/* Освобождаем память, которую мы уже выделили, поскольку деструктор для объекта никогда не будет вызван. Верхняя граница Еог-цикла соответствует индексу последнего элемента в массиве тСе11в, который мы пытались создать динамически (но попытка "проваливается"). Мы должны освободить память с помощью указателей, которые мы успели сохранить в массиве до возникновения исключения, используя все "занятые" индексы этого массива */
for (j = 0; j < i; j++) {delete [] mCells [j ] ;}
delete[] mCells;
//Любое исключение приводим к типу bad_alloc
throwbadalloc();
}
}
Необходимо обрабатывать все ошибочные ситуации, возникающие в самих деструкторах. Причины:
· Деструкторы могут работать, пока обрабатывается другое исключение. Если сгенерировать исключение из деструктора в то время, пока еще активно другое исключение, программа будет завершена.
· Подумайте о своих клиентах. Клиенты не вызывают деструкторы явно: они вызывают оператор delete, который вызывает деструктор. Если вы сгенерируете исключение из деструктора, то какие действия должен, по вашему, предпринять?
· Деструктор — это ваш шанс освободить память и другие ресурсы, используемые объектом. Если вы утратите этот шанс в результате преждевременного выхода из функции деструктора из-за исключения, вы никогда не сможете вернуться назад и освободить ресурсы.
Поэтому нужно стараться делать деструкторы как модно более простыми. Например, использовать только функции delete и delete[], которые не могут кинуть исключения.
Шаблоны
Шаблоны позволяют параметризовать не только значения, но и типы, позволяют применять классы к разным типам объектов.
С помощью шаблонов можно написать код, который не будет зависеть не только от задаваемых значений, но и от типов этих значений!
Например, вместо создания отдельных классов-стеков для хранения int-значений, объектов типа char и double, можно написать одно определение стекового класса, которое будет использоваться для любого из этих типов.
По историческим причинам для задания параметров-типов шаблона вместо ключевого слова typename можно использовать ключевое слово class. В результате во многих книгах и программах используется такой синтаксис: template<class Т>.
Спецификатор template действует всю последующую инструкцию.
Пример:
template<typename T>
class matrix{
T** _array;
int _width;
int _height;
public:
matrix(int width; int height);
operator+(matrix<T>);
matrix(const matrix<T>&);
};
Шаблонных параметров может быть несколько: template<typenameT, typenameU>
В качестве шаблонных параметров может быть задан размер матрицы
Тогда matrix<int, 4, 5> и matrix<int, 6, 7> - объекты разных классов.
!константные параметры должны быть в конце описания.
Для каждого параметра шаблона выполняется своя компиляция. То есть, при объявлении в коде программы объектов классов A<int> и A<char>, компилятор создаст 2 совершенно разных класса – один для типа int, а второй для типа char
Например, конструктор копирования может принимать в себя также шаблонный параметр:
template<typename T>
class matrix{
…
public:
template<typename U>
matrix(const matrix<U>&);
};
В описании конструктора будем выполнять приведение типа U к T.
STL: итераторы
Что такое итераторы?
Итератор – обобщенная абстракия, используемая для доступа к элементам контейнера.
Итератор можно представить себе как указатель на определенный элемент контенера.
Какие бывают итераторы?
Все стандартные контейнеры поддерживают либо двунаправленные итераторы, либо итераторы произвольного доступа.