Читайте также: |
|
Использование собственных классов исключений предпочтительнее применения стандартных типов данных. С помощью классов можно более гибко организовать передачу информации об исключении, легче дифференцировать обработку исключений, а кроме того, появляется возможность использовать иерархии классов.
Поскольку механизм управления исключениями позволяет создать обработчик для базового класса, родственные исключения часто можно представить в виде иерархии. Производя исключения от общего базового класса, можно в обработчике перехватывать ссылку или указатель на базовый класс, используя полиморфизм. Например, в математической библиотеке можно организовать классы следующим образом:
class Matherr{};
class Overflow: public Matherr{}; // Переполнение
class Underflow: public Matherr{}; // Исчезновение порядка
class ZeroDivide: public Matherr{}; // Деление на ноль
Для представления ошибок ввода/вывода могут использоваться следующие классы:
class I0err{};
class Readerr: public I0err{); // Ошибка чтения
class Writerr: public I0err{}; // Ошибка записи
class Seekerr: public I0err{}; // Ошибка поиска
В зависимости от обстоятельств можно использовать либо обработчик исключений базового класса, который будет перехватывать и производные исключения, либо собственные обработчики производных классов.
Существует ряд стандартных исключений, которые генерируются операциями или функциями C++. Все они являются производными от библиотечного класса exception, описанного в заголовочном файле <stdexcept>. Например, операция new при неудачном выделении памяти генерирует исключение типа bad_alloc.
Программист может определить собственные исключения, производные от стандартных.
ß//End “ Т.Павловская ”
Шаблоны классов /* Т. Павловская */
Синтаксис описания шаблона
template <описание_параметров_шаблона> определение_класса;
Параметры шаблона перечисляются через запятую. В качестве параметров могут использоваться типы, шаблоны и переменные.
Типы могут быть как стандартными, так и определенными пользователем. Для их описания используется ключевое слово class. Внутри шаблона параметр типа может применяться в любом месте, где допустимо использовать спецификацию типа, например:
template <class Data> class List{
class Node{
public:
Data d;
Node *next;
Node *prev;
Node(Data dat = 0){d = dat; next = 0; prev = 0;}
};
...
}
Класс Data можно рассматривать как формальный параметр, на место которого при компиляции будет подставлен конкретный тип данных.
Для любых параметров шаблона класса могут быть заданы значения по умолчанию, например: // во втором шаблоне первый используется как параметр
template<class T> class myarray { /*... */ };
...
template<class K, class V, template<class T> class С = myarray>
class Map{
C<K> key;
C<V> value;
...
};
Область действия параметра шаблона - от точки описания до конца шаблона, поэтому параметр можно использовать при описании следующих за ним, например:
template<class Т, Т* p, class U = Т> class X { /*... */ };
Методы шаблона класса автоматически становятся шаблонами функций. Если метод описывается вне шаблона, его заголовок должен иметь следующие элементы:
template <описание_параметров_шаблона>
возвр_тип имя_класса <параметры_шаблона >::
имя_функции (список_параметров функции)
Описание параметров шаблона в заголовке функции должно соответствовать шаблону класса, при этом имена параметров могут не совпадать (?пример?). Проще рассмотреть синтаксис описания методов шаблона на примере:
template <class Data> void List<Data>::print()
{ /* тело функции */ }
Здесь < class Data >- описание параметра шаблона, void - тип возвращаемого функцией значения, List - имя класса, < Data >- параметр шаблона, print - имя функции без параметров.
В случае нескольких параметров порядок их следования в описании_параметров и параметрах_шаблона должен быть один и тот же, например:
template<class T1, class T2> struct A{
void f1();
};
template<class T2, class T1> void A<T2, T1>::f1(){... }
Ниже перечислены правила описания шаблонов.
В качестве примера шаблона рассмотрим полное описание параметризованного класса двусвязного списка List.
template <class Data> class List{
class Node{
public:
Data d;
Node *next, *prev;
Node(Data dat = 0){d = dat; next = 0; prev = 0;}
};
Node *pbeg, *pend;
public:
List(){pbeg = 0; pend =0;}
~List();
void add(Data d);
Node * find(Data i);
Node * insert(Data key, Data d);
bool remove(Data key);
void print();
void print_back();
};
//--------Методы-------------------
template <class Data>
List <Data>::~List(){
if (pbeg!=0){
Node *pv = pbeg;
while (pv){
pv = pv->next; delete pbeg;
pbeg = pv;}
}
}
//---------------------------
template <class Data>
void List <Data>::print(){
Node *pv = pbeg;
cout << endl << "list: ";
while (pv){
cout << pv->d << ' ';
pv = pv->next;}
cout << endl;
}
//-------------------------------
template <class Data>
void List <Data>::print_back(){
Node *pv = pend;
cout << endl << " list back: ";
while (pv){
cout << pv->d << ' ';
pv = pv->prev; }
cout << endl;
}
//------------------------------
template <class Data>
void List <Data>::add(Data d){
Node *pv = new Node(d);
if (pbeg == 0)pbeg = pend = pv;
else{
pv->prev = pend;
pend->next = pv;
pend = pv;}
}
//------------------------------
template <class Data>
Node * List <Data>::find(Data d){
Node *pv = pbeg;
while (pv){
if(pv->d == d)break;
pv = pv->next;
}
return pv;
}
//------------------------------
template <class Data>
Node * List <Data>::insert(Data key, Data d){
if (Node *pkey = find(key)){
Node *pv = new Node(d);
pv->next = pkey->next;
pv->prev = pkey;
pkey->next = pv;
if(pkey!= pend)(pv->next)->prev = pv;
else pend = pv;
return pv;}
return 0;
}
//---------------------------------
template <class Data>
bool List <Data>::remove(Data key){
if(Node *pkey = find(key)){
if (pkey == pbeg){
pbeg = pbeg->next; pbeg->prev =0;}
else if (pkey == pend){
pend = pend->prev; pend->next =0;}
else {
(pkey->prev)->next = pkey->next;
(pkey->next)->prev = pkey->prev;}
delete pkey; return true;}
return false;
}
Если требуется использовать шаблон List для хранения данных не встроенного, а определенного пользователем типа, в описание этого типа необходимо добавить перегрузку операции вывода в поток и сравнения на равенство, а если для его полей используется динамическое выделение памяти, то и операцию присваивания.
При определении синтаксиса шаблона было сказано, что в него, кроме типов и шаблонов, могут передаваться переменные. Они могут быть целого или перечисляемого типа, а также указателями или ссылками на объект или функцию. В теле шаблона они могут применяться в любом месте, где допустимо использовать константное выражение. В качестве примера создадим шаблон класса, содержащего блок памяти определенной длины и типа:
template <class Type, int kol>
class Block{
public:
Block(){p = new Type [kol];}
~Block(){delete [] p;}
operator Type *();
protected:
Type * p;
};
//Метод (определение вне шаблона, перегруженная операция приведения типа)--
template <class Type, int kol>
Block <Type, kol>:: operator Type *(){
return p;
}
После создания и отладки шаблоны классов удобно помещать в заголовочные файлы.
Использование шаблонов классов
Чтобы создать при помощи шаблона конкретный объект конкретного класса (этот процесс называется инстанцированием), при описании объекта после имени шаблона в угловых скобках перечисляются его аргументы:
имя_шаблона <аргументы>
имя_объекта [(параметры_конструктора)];
Аргументы должны соответствовать параметрам шаблона. Имя шаблона вместе с аргументами можно воспринимать как уточненное имя класса. Примеры создания объектов по шаблонам, описанным в предыдущем разделе:
List <int> List_int;
List <double> List_double;
List <monstr> List_monstr;
Block <char, 128> buf;
Block <monstr, 100> stado;
При использовании параметров шаблона по умолчанию список аргументов может оказаться пустым, при этом угловые скобки опускать нельзя:
template<class T = char> class String;
String<>* p;
Если параметром шаблона является шаблон, имеющий специализацию (см. ниже), она учитывается при инстанцировании:
template<class T> class A{ // Исходный шаблон
int x;
};
template<class T> class A<T*> // Специализация шаблона
long x;
};
template<template<class U> class V> class C{
V<int> y;
V<int*> z;
};
...
C<A> c;
В этом примере V<int> внутри С<А> использует исходный шаблон, поэтому с.у.х имеет тип int, a V<int*> использует специализацию шаблона, поэтому с.z.x имеет тип long.
На месте формальных параметров, являющихся переменными целого типа, должны стоять константные выражения.
После создания объектов с помощью шаблона с ними можно работать так же, как с объектами обычных классов, например:
for (int i = 1; i<10; i++)List_double.add(i * 0.08);
List_double.print();
//---------------------------------------
for (int i = 1; i<10; i++)List monstr.add(i);
List_monstr.print();
//-------------------------------------
strcpy(buf, "Очень важное сообщение");
cout << but << endl;
Для упрощения использования шаблонов классов можно применить переименование типов с помощью typedef:
typedef List <double> Ldbl;
Ldbl List_double;
Специализация шаблонов классов
Каждая версия класса или функции, создаваемая по шаблону, содержит одинаковый базовый код; изменяется только то, что связано с параметрами шаблона. При этом эффективность работы версий, создаваемых для различных типов данных, может сильно различаться.
Если для какого-либо типа данных существует более эффективный код, можно либо предусмотреть для этого типа специальную реализацию отдельных методов, либо полностью переопределить (специализировать) шаблон класса.
Для специализации метода требуется определить вариант его кода, указав в заголовке конкретный тип данных. Например, если заголовок обобщенного метода print шаблона List имеет вид
template <class Data> void List <Data>::print();
специализированный метод для вывода списка символов будет выглядеть следующим образом:
void List <char>::print(){
... // Тело специализированного варианта метода print
}
Если в программе создать экземпляр шаблона List типа char, соответствующий вариант метода будет вызван автоматически.
При специализации целого класса после описания обобщенного варианта класса помещается полное описание специализированного класса, при этом требуется заново определить все его методы. Допустим, требуется специализировать шаблон Block, описанный ранее, для хранения 100 целых величин:
class Block<int, 100>{
public:
Block(){p = new int [100];}
~Block(){delete [] p;}
operator int *();
protected:
int * p;
};
Block<int, 100>::operator int *(){
return p;
}
При определении экземпляров шаблона Block с параметрами int и 100 будет задействован специализированный вариант.
Вообще-то переписать бы…эту муть
Дата добавления: 2015-11-16; просмотров: 43 | Нарушение авторских прав
<== предыдущая страница | | | следующая страница ==> |
Список исключений функции | | | Наследование классов |