
2 Визначення функцій-членів шаблона класу
Функції-члени шаблона класу можуть визначатися в тілі класу (тобто як вбудовані) або зовні тіла класу. В останньому випадку її визначення має вигляд:
template <class T1 ідентифікатор1,
T2 ідентифікатор2,...,
TN ідентифікаторN>
тип ім’я класу
template <class T1 ідентифікатор1,
T2 ідентифікатор2,...,
TN ідентифікаторN>
:: ім’я функції (список параметрів)
{
//Тіло функції
}
3 Використання шаблонів
Дуже часто шаблони використовуються для створення колекцій, тобто класів, що представляють з себе набір об'єктів одного і того ж типу. Найпростішим прикладів колекції буде масив. Назвемо нову структуру даних вектор vector.
template <class T>
class vector
{
public:
vector():nitem(0),items(0){};
~vector () {delete items;};
void insert (const T& t)
{ T* tmp = items;
items = new T[nitem + 1];
memcpy (items, tmp, sizeof(T) *nitem);
item[++nitem] = t;
delete tmp; }
void remove (void)
{ T * tmp = items;
items = new T [ --nitem];
memcpy (items, tmp, sizeof (T) *nitem);
delete tmp;
}
const T& operator [] (int index) const
{
if ((index < 0) || (index >= nitem))
throw IndexOutOfRange;
return items[index];
}
T& operator[](int index)
{
if ((index < 0) || (index >= nitem))
throw IndexOutOfRange;
return items[index];
}
private:
Т* items;
int nitem;
};
Окрім конструктора і деструктора у нашого вектора є тільки три методи: метод insert додає в кінець вектора новий елемент, збільшуючи довжину вектора на одиницю, метод remove видаляє останній елемент вектора, зменшуючи його довжину на одиницю, і операція [.] звертається до n-ому елемента вектора.
Параметр шаблона vector - будь-який тип, у якого визначені операція привласнення і стандартний конструктор. Згенерувати конкретний клас з шаблона можна явно, записавши:
template vector<int>;
Цей оператор не створить ніяких об'єктів типу vector<int> але проте викличе генерацію класу зі всіма його методами.
Одним з цікавих методів використовування шаблонів є уточнення поведінки за допомогою додаткових параметрів шаблона. Припустимо, ми пишемо функцію сортування вектора:
template <class T>
void sort_vector(vector<T>& vec)
{
for (int i = 0; i < vec.size() -1; i++)
for (int j = i; j < vec.size(); j++) {
if (vec[i] < vec[j]) {
Т tmp = vec[i];
vec[i]= vec[j];
vec[j]= tmp;
}
}
}
Ця функція працюватиме добре з числами, але якщо ми схочемо її використовувати для масиву вказівників на рядки (char*), то результат буде не зовсім очікуваний. Сортування відбудеться не по значенню рядків, а за їх адресами (операція менше для двох вказівників -це порівняння значень цих вказівників, тобто адрес величин, на які вони вказують, а не самих величин). Щоб виправити недолік, додамо другий параметр до шаблону:
template <class Т, class Compare>
void sort__vector (vector<T>& vec)
{
for (int i = 0; i < vec.size() - 1; i++)
for (int j = i; j < vec.size(); j++) {
if (Compare:: less (vec [i], vec[j])){
Т tmp = vec [i];
vec [i] = vec[ j ];
vec[j]= tmp;
}
}
}
Клас Compare повинен реалізовувати статичну функцію less, що порівнює два значення типу T. Для цілих чисел цей клас може виглядати таким чином:
class Comparelnt
static bool less(int а, int b) { return а < b; };
і сортування вектора буде мати вигляд
vector<int> vec;
sort<int, Compareint>(vec);
Для вказівників на байт (рядків) можна створити клас class CompareCharStr
static bool less(char* а, char* b)
{ return strcmp(а,b) >= 0); };
і, відповідно, сортувати за допомогою виклику
vector<char*> svec;
sort<char*, CompareCharStr>(svec);
Як легко помітити, для всіх типів, для яких операція менше має потрібний нам сенс, можна написати шаблон класу порівняння:
template<class T> Compare
static bool less (Т а, T b) { return а < b; };
і використовувати його в сортуванні (зверніть увагу на пропуск між кутовими дужками, що закриваються, в параметрах шаблона; якщо його не поставити, компілятор спутає дві дужки з операцією зсуву):
vector<double> dvec;
sort<double, Compare<double> >(dvec);
Щоб не захаращувати запис, скористаємося можливістю задання значення параметра за замовчуванням. Так само, як і для аргументів функцій і методів, для параметрів шаблона можна визначити значення за замовчуванням. Остаточний вид функції сортування буде наступний:
template <class Т, class C = Compare<T> >
void sort_vector(vector<T>& vec)
{
for (int i = 0; i < vec.size() - 1; i++)
for (int j = i; j < vec.size (); j++) {
if (C::less(vec[i], vec[j])) {
Т tmp = vec[i];
vec[i]= vec[j];
vec [ j] = tmp;
}
}
}
Другий параметр шаблона іноді називають параметром-штрих, оскільки він лише модифікує поведінку класу, який маніпулює з типом, який визначений першим параметром.