Передача в шаблон класса дополнительных параметров
Шаблоны
Параметризированные классы
Параметризированный класс – некоторый шаблон, на основе которого можно строить другие классы. Этот класс можно рассматривать как некоторое описание множества классов, отличающихся только типами их данных. В С++ используется ключевое слово template для обеспечения параметрического полиморфизма. Параметрический полиморфизм позволяет использовать один и тот же код относительно различных типов (параметров тела кода). Это наиболее полезно при определении контейнерных классов. Шаблоны определения класса и шаблоны определения функции позволяют многократно использовать код, корректно по отношению к различным типам, позволяя компилятору автоматизировать процесс реализации типа.
Шаблон класса определяет правила построения каждого отдельного класса из некоторого множества разрешенных классов.
Спецификация шаблона класса имеет вид:
template <список параметров>
Class объявление класса
Список параметров класса-шаблона представляет собой идентификатор типа, подставляемого в объявление данного класса при его генерации. Рассмотрим пример шаблона класса работы с динамическим массивом и выполнением контроля за значениями индекса при обращении к его элементам.
#include "iostream.h"
#include "string.h"
template <class T>
Class vector
{ T *ms;
int size;
public:
vector() : size(0),ms(NULL) {}
~vector(){delete [] ms;}
void decrem(const T &t) // увеличение размера массива на 1 элемент
{ T *tmp = ms;
ms=new T[size+1]; // ms - указатель на новый массив
if(tmp) memcpy(ms,tmp,sizeof(T)*size); // перезапись tmp -> ms
ms[size++]=t; // добавление нового элемента
if(tmp) delete [] tmp; // удаление временного массива
}
void inkrem(void) // уменьшение размера массива на 1 элемент
{ T *tmp = ms;
if(size>1) ms=new T[--size];
if(tmp)
{ memcpy(ms,tmp,sizeof(T)*size); // перезапись без посл.элемента
delete [] tmp; // удаление временного массива
}
}
T &operator[](int ind) // определение обычного метода
{ // if(ind<0 || (ind>=size)) throw IndexOutOfRange; // возбуждение
// исключительной ситуации IndexOutOfRange
return ms[ind];
}
};
void main()
{ vector <int> VectInt;
vector <double> VectDouble;
VectInt.decrem(3);
VectInt.decrem(26);
VectInt.decrem(12); // получен int-вектор (массив) из 3 атрибутов
VectDouble.decrem(1.2);
VectDouble.decrem(.26);//получен double-вектор (массив) из 2 атрибутов
int a=VectInt[1]; // a = ms[1]
cout << a << endl;
int b=VectInt[4]; // будет возбуждена исключительная ситуация
cout << b << endl;
double d=VectDouble[0];
cout << d << endl;
VectInt[0]=1;
VectDouble[1]=2.41;
}
Класс vector наряду с конструктором и деструктором имеет 2 функции: decrem – добавление в конец вектора нового элемента, inkrem – уменьшение числа элементов на единицу и операция [] обращения к i-му элементу вектора.
Параметр шаблона vector – любой тип, у которого определены операция присваивания и операция new. Например, при задании объекта типа vector <int> происходит генерация конкретного класса из шаблона и конструирование соответствующего объекта VectInt, при этом тип Т получает значение типа int. Генерация конкретного класса означает, что генерируются все его компоненты-функции, что может привести к существенному увеличению кода программы.
Выполнение функций
VectInt.decrem(3);
VectInt.decrem(26);
VectInt.decrem(12);
приведет к созданию вектора (массива) из трех атрибутов (3, 26 и 12 ).
Сгенерировать конкретный класс из шаблона можно, явно записав:
template vector<int>;
При этом не будет создано никаких объектов типа vector<int>, но будет сгенерирован класс со всеми его компонентами.
В некоторых случаях желательно описания некоторых компонент-функций шаблона класса выполнить вне тела шаблона, например:
#include "iostream.h"
template <class T1,class T2>
T1 sm1(T1 aa,T2 bb) // описание шаблона глобальной
{ return (T1)(aa+bb); // функции суммирования значений
} // двух аргументов
template <class T1,class T2>
Class cls
{ T1 a;
T2 b;
public:
cls(T1 A,T2 B) : a(A),b(B) {}
~cls(){}
T1 sm1() // описание шаблона функции
{ return (T1)(a+b); // суммирования компонент объекта obj_
}
T1 sm2(T1,T2); // объявление шаблона функции
};
template <class T1,class T2>
T1 cls<T1,T2>::sm2(T1 aa,T2 bb) // описание шаблона функции
{ return (T1)(aa+bb); // суммирования внешних данных
}
void main()
{ cls <int,int> obj1(3,4);
cls <double,double> obj2(.3,.4);
cout<<"функция суммирования компонент объекта 1 = "
<<obj1.sm1()<<endl;
cout<<"функция суммирования внешних данных (int,int) = "
<<obj1.sm2(4,6)<<endl;
cout<<"вызов глобальной функции суммирования (int,int) = "
<<sm1(4,.6)<<endl;
cout<<"функция суммирования компонент объекта 2 = "
<<obj2.sm1()<<endl;
cout<<"функция суммирования внешних данных (double,double)= "
<<obj2.sm2(4.2,.1)<<endl;
}
Передача в шаблон класса дополнительных параметров
При создании экземпляра класса из шаблона в него могут быть переданы не только типы, но и переменные и константные выражения:
#include "iostream.h"
template <class T1,int i=0,class T2>
Class cls
{ T1 a;
T2 b;
public:
cls(T1 A,T2 B) : a(A),b(B){}
~cls(){}
T1 sm() //описание шаблона ф-ции суммирования компонент объекта
{ // i+=3; // error member function 'int __thiscall cls<int,2>::sm(void)'
return (T1)(a+b+i);
}
};
void main()
{ cls <int,1,int> obj1(3,2); // в шаблоне const i инициализируется 1
cls <int,0,int> obj2(3,2,1); // error 'cls<int,0>::cls<int,0>':no overloaded
// function takes 3 parameter s
cls <int,int,int> obj13(3,2,1); // error 'cls' : invalid template argument for 'i',
// constant expression expected
cout<<obj1.sm()<<endl;
}
Результатом работы программы будет выведенное на экран число 6.
В этой программе инструкция template <class T1,int i=0,class T2> говорит о том, что шаблон класса cls имеет три параметра, два из которых - имена типов (Т1 и Т2), а третий (int i=0) - целочисленная константа. Значение константы i может быть изменено при описании объекта cls <int,1,int> obj1(3,2).
Шаблоны функций
В С++, так же как и для класса, для функции (глобальной, то есть не являющейся компонентой-функцией) может быть описан шаблон. Это позволит снять достаточно жесткие ограничения, накладываемые механизмом формальных и фактических параметров при вызове функции. Рассмотрим это на примере функции, вычисляющей сумму нескольких аргументов.
#include "iostream.h"
#include "string.h"
template <class T1,class T2>
T1 sm(T1 a,T2 b) // описание шаблона
{
return (T1)(a+b); // функции c 2 параметрами
}
template <class T1,class T2,class T3>
T1 sm(T1 a,T2 b,T3 c) // описание шаблона функции
{ return (T1)(a+b+c); // функции c 3 параметрами
}
void main()
{cout<<"вызов ф-ции суммирования sm(int,int) = "<<sm(4,6)<<endl;
cout<<"вызов ф-ции суммирования sm(int,int,int) = "<<sm(4,6,1)<<endl;
cout<<"вызов ф-ции суммирования sm(int,double) = "<<sm(5,3)<<endl;
cout<<"вызов ф-ции суммирования sm(double,int,short)= " <<
sm(.4,6,(short)1)<<endl;
// cout<<sm("я изучаю","язык С++")<<endl; error cannot add two pointers
}
В программе описана перегруженная функция sm(), первый экземпляр которой имеет 2, а второй 3 параметра. При этом тип формальных параметров функции определяется при вызове функции типом ее фактических параметров. Используемые типы Т1, Т2, Т3 заданы как параметры для функции с помощью выражения template <class T1,class T2,class T3>. Это выражение предполагает использование типов Т1, Т2 и Т3 в виде ее дополнительных параметров. Результат работы программы будет иметь вид:
вызов функции суммирования sm(int,int) = 10
вызов функции суммирования sm(int,int,int) = 11
вызов функции суммирования sm(int,double) = 8
вызов функции суммирования sm(double,int,short)= 7.4
В случае попытки передачи в функцию sm() двух строк, то есть типов, для которых не определена данная операция, компилятор выдаст ошибку. Чтобы избежать этого, можно ограничить использование шаблона функции sm(), описав явным образом функцию sm() для некоторых конкретных типов данных. В нашем случае:
char *sm(char *a,char *b) // явное описание функции объединения
{ char *tmp=a; // двух строк
a=new char[strlen(a)+strlen(b)+1];
strcpy(a,tmp);
strcat(a,b);
return a;
}
Добавление в main() инструкции, например,
cout<<sm("я изучаю"," язык С++")<<endl;
приведет к выводу кроме указанных выше сообщения:
я изучаю язык С++
Следует отметить, что шаблон функции не является ее экземпляром. Только при обращении к функции с аргументами конкретного типа происходит генерация конкретной функции.