Lucture2_4_Templates.ppt
- Количество слайдов: 20
Шаблоны классов Параметризованный класс создает семейство родственных классов, которые можно применять к любому типу данных, передаваемому в качестве параметра. Шаблоны широко применяют при создании контейнерных классов. Контейнерным называется класс, который предназначен для хранения каким либо образом организованных данных и работы с ними. Преимущество использования шаблонов состоит в том, что как только алгоритм работы с данными определен и отлажен, он может применяться к любым типам данных без переписывания кода.
Пример: “не параметризованный” список class Node{ public: int d; // Данные Node *next: // Указатель на последующий узел Node *prev; // Указатель на предыдущий узел Node(int dat = 0){ // Конструктор d = dat; next = 0; prev = 0; } };
Пример: “не параметризованный” список class List{ class Node{…}; Node *pbeg, *pend; // Указатели на начало и конец списка public: List()(pbeg = 0; pend = 0; } // Конструктор ~List(); // Деструктор void add(int d): // Добавление узла в конец списка Node * find(int i); // Поиск узла по ключу // Вставка узла d после узла с ключом key: Node * insert(int key, int d); bool remove(int key); // Удаление узла void print(); // Печать списка в прямом направлении void print_back(); // Печать списка в обратном направлении }:
Пример: “не параметризованный” список – реализация методов void List: : add(int d){ Node *pv = new Node(d); // Выделение памяти под новый узел if (pbeg == 0) pbeg = pend = pv; // Первый узел списка else{ // Связывание нового узла с предыдущим: pv >prev = pend; pend >next = pv; pend = pv; } // Обновление указателя на конец списка } // Функция поиска узла с заданным ключом: Node * List: : find( int d ){ Node *pv = pbeg; while (pv){ If (pv >d == d)break; pv = pv >next; } return pv; }
Пример: “не параметризованный” список – реализация методов //Метод insert вставляет в список узел после узла с ключом key и возвращает //указатель на вставленный узел. Node * List: : insert(int key, int d){ If (Node *pkey = find(key)) { // Поиск узла с ключом 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; }
Пример: “не параметризованный” список – реализация методов // Метод remove удаляет узел с заданным ключом из списка bool List: : remove(int 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; }
Пример: “не параметризованный” список – реализация методов void List: : print(){ Node *pv = pbeg; cout << endl << “List: “; while (pv){ cout << pv >d << ' ‘; pv = pv >next; } cout << endl; } void List: : print_back(){ Node *pv = pend; cout << endl << " List back: "; while (pv){ cout << pv >d << ' ‘; pv = pv >prev; } cout << endl; }
Пример: “не параметризованный” список – реализация методов // Деструктор списка освобождает память из под всех его элементов: List: : ~List(){ if (pbeg != 0){ Node *pv = pbeg; while (pv){ pv = pv >next; delete pbeg; pbeg = pv; } } } // Пример использования в программе: int main(){ List L; for (int i = 2; i<6; i++) L. add(i); L. print(); L. print_back(); L. insert(2, 200); if (!L. remove(5)) cout << "not found“; L. print(); L. print__back(); }
Синтаксис описания шаблона 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; } }: }
Синтаксис описания шаблона Для любых параметров шаблона могут быть заданы значения по умолчанию, например: template<class Т> class myarray {/*…*/}; … template <сlass K, class V, template<class T> class С = myarray> class Map{ C<K> key; С<V> value; }; Область действия параметра шаблона — от точки описания до конца шаблона, поэтому параметр можно использовать при описании следующих за ним, например: template<class Т, Т* p, class U = Т> class X { /*. . . */ };
Синтаксис описания шаблона Методы шаблона класса автоматически становятся шаблонами функций. Если метод описывается вне шаблона, его заголовок должен иметь следующие элементы: template<описание_параметров_шаблона> возвр_тип имя_класса <параметры_шаблона>: : имя_функции (список_параметров функции) Пример: template <class Data> void List<Data>: : print() { /* тело функции */ } l l l <class Data>— описание параметра шаблона, void — тип возвращаемого функцией значения, List — имя класса, <Data>— параметр шаблона, print — имя функции без параметров.
Синтаксис описания шаблона В случае нескольких параметров порядок их следования в описании_параметров и параметрах_шаблона должен быть один и тот же, например: template<class T 1, class T 2> struct A{ void f(); }; template<c!ass T 2, class T 1> void A<T 2, T 1> : : f( ) {. . . }
Правила описания шаблонов: l Локальные классы не могут содержать шаблоны в качестве своих элементов. l Шаблоны методов не могут быть виртуальными. l Шаблоны классов могут содержать статические элементы, дружественные функции и классы. l Шаблоны могут быть производными как от шаблонов, так и от обычных классов, а также являться базовыми и для шаблонов, и для обычных классов. l Внутри шаблона нельзя определять friend шаблоны.
Пример: параметризованный двусвязный список 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 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_jnt; List <double> List_double; List <monstr> List_monstr; Block <char, 128> buf; Block <monstr, 100> stado; При использовании параметров шаблона по умолчанию список аргументов может оказаться пустым, при этом угловые скобки опускать нельзя: template<class Т = char> class String; String<>* p;
Специализация шаблонов классов Для специализации метода требуется определить вариант его кода, указав в заго ловкеконкретный тип данных. Например, если заголовок обобщенного метода print шаблона List имеет вид template <class Data> void List <Data>: : print(); специализированный метод для вывода списка символов будет выглядеть сле дующим образом: void List <char>: : print(){. . . // Тело специализированного варианта метода print} При специализации целого класса после описания обобщенного варианта класса помещается полное описание специализированного класса, при этом требуется заново определить все его методы. Допустим, требуется специализировать шаб лон. Block, описанный ранее, для хранения 100 целых величин: class Block<int, 100>{ public: Block(){p = new int [100]; } ~Block. O{delete [ ] p; } operator int *(); protected: int * p; }; Block<int, 100>: : operator int *(){ return р; }
Lucture2_4_Templates.ppt