Теоретическое введение. С помощью шаблонов можно создавать родовые (generic) функции и классы. Родовая функция определяет базовый набор операций, которые будут применяться к разным типам данных, получаемых функцией в качестве параметра.
Определение функции с ключевым словом template (шаблон) имеет вид:
template<class Ttype >тип имя_функции(список аргументов)
{//тело функции}
Здесь Ttype – фиктивный тип, который используется при объявлении аргументов и локальных переменных функции. Компилятор заменит этот фиктивный тип на один из реальных и создаст соответственно несколько перегружаемых функций. При этом перегружаемые функции являются ограниченными, поскольку выполняют одни и те же действия над данными различных типов.
С помощью шаблона класса можно создать класс, реализующий стек, очередь, дерево и т. д. для любых типов данных. Компилятор будет генерировать правильный тип объекта на основе типа, задаваемого при создании объекта. Общая форма объявления шаблона класса:
template <class Ttype > class имя_класса {
Имена класса, поля класса
}
Здесь Ttype – фиктивное имя типа, которое заменится компилятором на фактическое. Шаблон класса может иметь больше одного родового типа данных:
template<class Type1,class Type2> class m {Type1 a;Type2 b;}
Пример. Создается шаблон класса для работы с односвязным списком. Тестирование класса выполняется с использованием меню, которое позволяет создать список из чисел типов integer, float, double и выполнить типичные операции с ним.
#include <conio.h>
#include <string.h>
#include <iostream.h>
#include"vip\menu.cpp"
void ListForInt();
void ListForFloat();
void ListForDouble();
char bufRus[256];
char*Rus(const char*text){
CharToOem(text,bufRus);
return bufRus;
}
//шаблон класса для работы с односвязным списком
template<class mytype>class List {
//внутренний класс, для представления элементов списка
class Node{
public:
mytype d;
Node* next;
Node(mytype dat=0){d=dat; next=0;}
};
Node* pbeg; //указатель на начало списка
public:
List(){pbeg=0;} //конструктор
~List(); //деструктор
Node * Add(mytype d); //добавление в конец списка
Node * Find(mytype key); //поиск по ключу
Node * Insert(mytype key,mytype d); //вставка узла d после узла с
// ключом key
bool Remove(mytype key); //удаление узла
void Print(); //печать списка
};
//*********************~List() *************************
//ДЕСТРУКТОР. Освобождает память для всех узлов списка
template<class mytype> List<mytype>::~List(){
if(pbeg!=0){
Node* pv=pbeg;
while(pv){
pv=pv->next;
delete pbeg;
pbeg=pv;
}
}
}
//*************************** void Add(mytype d) **********
//Добавляет узел в конец списка и возвращает указатель
// на вставленный узел.
template<class mytype> List<mytype>::Node*
List<mytype>::Add(mytype d){
Node* pv=new Node(d); //Создание нового узла
if(pbeg==0)pbeg=pv; //первый узел списка
else {
Node* rab=pbeg;
while(rab!=0){
if((rab->next)==0){rab->next=pv;return pv;}
rab=rab->next;
}
}
}
//*************************** Node* Find(mytype key)
//Выполняет поиск узла с заданным ключом и возвращает указатель
// на него в случае успешного поиска и 0 в случае отсутствия узла в списке
template<class mytype> List<mytype>::Node*
List<mytype>::Find(mytype key){
Node* pv=pbeg;
while(pv){
if((pv->d)==key)break;
pv=pv->next;
}
return pv;
}
//************* Node* Insert(mytype key,mytype d)
//Вставляет в список узел после узла с ключом key и возвращает
// указатель на вставленный узел. Если такого узла в списке нет,
// вставка не выполняется и возвращается значение 0
template<class mytype> List<mytype>::Node*
List<mytype>::Insert(mytype key,mytype d){
if(Node* pkey=Find(key)) //поиск узла с ключом key
{
Node* pv=new Node(d);
//выделение памяти под новый узел и его инициализация
pv->next=pkey->next;
//установление связи нового узла с последующим
pkey->next=pv; //установление связи предыдущего узла с новым
return pv;
}
return 0;
}
//******************* bool Remove(mytype key)
//Удаляет узел с заданным ключом из списка и возвращает значение true при
//успешном удалении и false, если узел с таким ключом не найден
template<class mytype> bool List<mytype>::Remove(mytype key){
if(Node* pkey=Find(key)){
if(pkey==pbeg)pbeg=pbeg->next; //удаление из начала списка
else{ //Находим указатель на узел,
Node*rab=pbeg; //стоящий в списке перед
while(rab) //удаляемым узлом.
{ //rab-этот указатель.
if((rab->next)==pkey)break;
rab=rab->next;
}
rab->next=pkey->next;
}
delete pkey;
return true;
}
return false;
}
//******************** void Print() -Печать списка
template<class mytype> void List<mytype>::Print(){
Node*pv=pbeg;
cout<<Rus("Наш список:");cout<<endl;
while(pv){
cout<<pv->d<<' ';
pv=pv->next;
}
cout<<endl;}
//--------------------------- MAIN ---------------------------------------------
int main(int argc, char* argv[]){
int k=0,max,kol;
char menu[][100]= {{" ListForInt "}, {" ListForFloat "},
{" ListForDouble "}, {" EXIT "}, };
kol=4; //КОЛИЧЕСТВО СТРОК МЕНЮ. Это используется в выравнивании
//строк меню по центру.
//----ВЫРАВНИВАНИЕ СТРОК МЕНЮ ПО ЦЕНТРУ------------------
max=viravnivaniestrok(menu,kol);
//----------------- МЕНЮ НА ЭКРАНЕ---------------------------------------
textmode(C80);
while(1){
switch(mmm(kol,menu,max,k))
{ case 0: {
ListForInt();
k=0;break;
}
case 1: {
ListForFloat();
k=1;break;
}
case 2: {
ListForDouble();
k=2;break;
}
case 3:{
exit(0);
}
}
}
return 0;
}
//*************************** void ListForInt()
//Эта функция вызывается из главного меню.
void ListForInt(){
List<int>l1;
int k=0,max,kol;
char menu[][100]=
{ {" PrintList "}, {" Add "}, {" Find "}, {" Insert "},
{" Remove "}, {" EXIT "}, {" Back "} };
kol=7; //КОЛИЧЕСТВО СТРОК МЕНЮ.
max=viravnivaniestrok(menu,kol);
//------------------------ МЕНЮ НА ЭКРАНЕ-----------------------------
textmode(C80);
while(1){
switch(mmm(kol,menu,max,k))
{ case 0: {
l1.Print();
while(!kbhit())
k=0;break;}
case 1: {
cout<<Rus("введите число, которое надо вставить:");
int t;cin>>t;
if((l1.Add(t)))cout<<Rus("вставка осуществлена");
else cout<<Rus("вставка не осуществлена");
while(!kbhit());
k=1;break;}
case 2: {
cout<<Rus("введите искомое число:");
int t;
cin>>t;
if(l1.Find(t))cout<<Rus("искомое число есть в списке.");
else cout<<Rus("искомого числа нет в списке.");
while(!kbhit());
k=2;break;}
case 3: {
cout<<Rus("введите число, которое надо вставить:");
int t;cin>>t;
cout<<Rus("введите число, после которого надо вставить:");
int key;cin>>key;
if((l1.Insert(key,t)))cout<<Rus("вставка осуществлена");
else cout<<Rus("вставка не осуществлена");
while(!kbhit());
k=3;break;}
case 4: {
cout<<Rus("введите число, которое надо удалить:");
int t;cin>>t;
if((l1.Remove(t)))cout<<Rus("удаление осуществлено");
else cout<<Rus("такого числа нет в списке.");
while(!kbhit());
k=4;break;}
case 5:{exit(0);}
} } }
Таким же образом, как и функция ListForInt(), реализуются функции ListForFloat() и ListForDouble(), предназначенные для тестирования списков из чисел типа float и double, соответственно.
Задания для самостоятельного решения
Для разработки шаблонов классов можно использовать результаты выполнения лабораторных работ № 2 и № 3. При тестировании со-зданных шаблонов классов необходимо создавать объекты с различными допустимыми значениями параметров шаблона (например, компоненты вектора могут быть целыми, действительными или комплексными числами).
1. Создать шаблон класса для работы со стеком. Применить его для решения задач № 1 – 4 (лаб. работа № 3).
2. Создать шаблон класса для работы с одномерным массивом. Выполнить тестирование путем создания и обработки массивов, содержащих элементы различных типов (например, сортировка элементов массивов различными методами).
3. Создать шаблон класса Vector размерности n (см. задачу № 3, лаб. работа № 2).
4. Создать шаблон класса «Квадратная матрица» – Matrix размерности n´n (см. задачу № 4, лаб. работа № 2).
5. Создать шаблон класса Polynom степени n (см. задачу № 5, лаб. работа № 2) или создать шаблон класса для работы с односвязным списком. Применить его для решения задачи № 5 (лаб. работа № 3).
6. Создать шаблон класса для работы с односвязным списком. Применить шаблон класса для решения задачи № 6 (лаб. работа № 3).
7. Создать шаблон класса для работы с односвязным списком. Применить шаблон класса для решения задачи № 7 (лаб. работа № 3).
8. Создать шаблон класса для работы с бинарным деревом. Применить его для сортировки действительных чисел и строк, вводимых с клавиатуры или из файла.
9. Создать шаблон класса Set (множество) мощности n (см. задачу № 9, лаб. работа № 2) или создать шаблон класса для работы с односвязным списком. Применить шаблон класса для решения задачи № 9 (лаб. работа № 3).
10. Создать шаблон класса для работы с односвязным списком. Применить шаблон класса для решения задачи № 10 (лаб. работа № 3).
11. Создать шаблон класса, обеспечивающего описание матрицы заданного размера n´m и любого минора в ней (см. задачу № 11, лаб. работа № 2) или создать шаблон класса для работы с двусвязным списком. Применить шаблон класса для решения задачи № 11 (лаб. работа № 3).
12. Создать шаблон класса для работы с бинарным деревом. Применить шаблон класса для решения задачи № 12 (лаб. работа № 3).
13. Создать шаблон класса для работы с бинарным деревом. Применить шаблон класса для решения задачи № 13 (лаб. работа № 3).
14. Создать шаблон класса для работы с двусвязным списком. Применить шаблон класса для решения задачи № 15 (лаб. работа № 3).
15. Создать шаблон класса для работы с односвязным списком. Применить шаблон класса для решения задачи № 16 (лаб. работа № 3).
Тесты
1. Отметьте все утверждения, которые считаете верными:
1) нельзя с помощью шаблона создать функцию с таким же именем, как у явно определенной функции;
2) цель введения шаблонов – создание функций, которые могут обрабатывать однотипные данные;
*3) при использовании шаблонов функций возможна перегрузка как с помощью шаблонов, так и функций;
*4) в качестве описания шаблона функции используется прототип шаблона:
2. Можно ли задать шаблон со значением параметра по умолчанию?
Варианты ответа:
*1) да; 2) нет.
3. Каков правильный заголовок шаблона?
Варианты ответа:
*1) template<class t1,class t2>; 2) template <class t1,t2>; 3) template <class t,class t>;
4. Сколько параметров может быть у шаблона при определении шаблона функции?
Варианты ответа:
1) 1; 2) столько, сколько аргументов у функции; *3) столько, сколько типов используется для параметризации.
5. Отметьте правильный вариант описания шаблона семейства функций:
1) template(class T)
void func(T* p1,T* p2){…}
2) template <class T>;
void func(T* p1,T* p2){…}
*3) template<class T>
void func(T* p1,T* p2){…}
4) template<class T>
void func(T* p1,T* p2){…}
6. Можно ли использовать класс-шаблон в качестве базового класса?
Варианты ответа:
*1) да; 2) нет.