1.2. Шаблоны классов
Шаблоны классов определяются как тип данного с заранее неизвестными типами полей и других элементов класса, и также позволяют работать с данными разных типов.
Синтаксис:
template < параметры_шаблона >
определение_класса
В таком определении объявляется семейство классов. Параметры_шаблона, это обобщенные имена типов.
Отличие от шаблонов функций
При объявлении объекта класса необходимо указать фактический параметр - имя типа в угловых скобках <>. Это необходимо для создания реального объекта, где фактическим параметром будет тип данных объекта класса.
Пример - шаблон класса "массив". Тип элементов массива может быть любой, в том числе, отличный от базовых типов.
Механизм шаблона классов
При создании объекта класса определяется, объект какого именно класса, порожденного из шаблона, будет создан.
Это достигается подстановкой фактических параметров шаблона:
имя_класса <фактические_параметры> имя_объекта(м.быть_параметры_конструктора)
Например, My_Class <int> A (10);
Замечания и выводы
1. Параметр шаблона фактически заменяет произвольный тип. При формальном описании класса параметр шаблона используется как обобщенное имя типа.
Механизм: Шаблоны автоматически расширяются компилятором до полного описания классов.
array <int> c(20);
array <float> f(20);
2. Функции класса, объявленные и описанные внутри, являются inline.
Эти объявления можно перенести вне тела шаблона, тогда используется полное объявление шаблона функции, например:
template <class Type> Type имя_класса <Type>::имя_функции(...)
{
тело функции;
}
Дополнительно "имя_класса <Type>" - параметр шаблона.
3. Совпадение сигнатуры и перегрузка.
Родовая функция не может работать в специальных случаях. Например, Swap для базовых типов реализуется элементарно, для строк - иначе, для данных, являющихся объектами классов - иначе.
В этом случае используется перегрузка функции (или операции) для данного нестандартного типа. Допускается перегрузка по типу параметров и по типу функции.
Например, Swap для обмена значений двух строк будет выглядеть так:
void Swap ( char *s1,char *s2)
{
int len = (strlen(s1)>=strlen(s2)) ? strlen (s1): strlen (s2);
char *tmp = new char [len+1];
strcpy(tmp,s1);
strcpy(s1,s2);
strcpy(s2,tmp);
delete tmp;
}
4.Перегрузка операций для шаблонов.
Хочется воспользоваться готовыми шаблонами, применяя их к новым типам данных.
Например, Swap успешно переменяет местами данные базовых типов, и, возможно, данные производных типов, потому что конструктор копирования существует по умолчанию.
Сортировка для массива объектов требует:
Использовать операцию отношения < или > для сравнения объектов.
Использовать операцию = для присваивания нулю.
Во многих случаях требуется также перегружать конструктор копирования.
Вывод - если шаблон того требует, то операции перегружают.
My_class A,B;
// Прямое копирование.
A = B;
My_class *A = new My_Class; //
My_class *B = new My_Class;
// Поверхнстное копирование.
A = B;
5. Шаблоны классов могут содержать друзей.
friend функция, не использующая спецификацию шаблона, будет дружественной для всех экземпляров шаблона класса.
friend функция с параметрами шаблона будет дружественной только для того класса, экземпляр которого фактически создается.
Например,
template <class T>
class Matrix { // Шаблон матрицы типа Т.
…
friend void norma (); // Универсальная функция.
…
friend Vector <T> Sum (Vector <T> V); // Экземпляр для класса.
…
} // End of Matrix
Прочие особенности дружественных функций сохраняются.
6. Статические элементы не универсальны, а являются собственными данными для каждой реализации объекта.
Например,
template <class Type> class F
{
public:
static int count;
...
};
// Инициализация статического данного
template <class Type> int F::count = 0;
Type <int> A;
Type <char> B;
Определены две статические переменные F <int>::count и F <char>::count.
template <class Type> class F
{
static int i; // Обычное статическое данное.
static Type t; // Параметризованное данное.
...
};
// Определить статическое данное:
template <class Type> int A <Type>::i=0;
template <class Type> Type B <Type>::t=0;
Type <char> A; // Имеет int A::i и char A::t;
Type <float> B; // Имеет int B::i и float B::t;
7. Число аргументов шаблона (функции или класса) не ограничено, типы могут быть различны.
Пример - можно параметризировать тип элементов массива и число данных в нем:
template <int n, class T> class Array
{
public:
T a[n];
...
};
Array <50,double> x,y;
...
x = y; // Должно работать.
Нетипизированные параметры шаблона должны быть заменены фактическими константными выражениями.
8. Наследование.
Параметризованные классы могут использоваться в схеме наследования и комбинироваться в различных формах. Класс шаблон может быть порожден из обычного. Обычный класс может быть порожден от шаблона.
Пример Template_class консольный.
