Моё почтение,
господа. Сегодня речь
пойдет о такой интересной вещи как шаблоны (иногда их называют
контейнеры). Как всегда начнем статью с примера, который обоснует
необходимость применения темплейтов. Допустим, у нас есть класс
массива: |
classs cArray{ public: int *Array; //интерфейсные функции };
|
Очень хороший у нас
получился класс :). А
если нам захочется такой же класс, но уже для float’а,
double’а, char’а? Для каждого типа написание своего
класса будет нерациональной тратой времени. Можно конечно сделать
массив char’ов
в который с помощью memcpy будем закатывать все что нам нужно, но при
таком подходе в программе мы будем обязаны постоянно приводить типы,
лишней работы хотелось бы избежать, так ведь? Тогда у нас нет другого
выхода, кроме как воспользоваться шаблонами. Приведенный выше класс
преобразится следующим образом: |
template< class Type >class cArray{ public: Type *Array; //интерфейсные функции Type &operator[]( int ); };
template< class Type >Type &cArray<Type>::operator[]( int i ){return(Array[i]);}
|
Обратите внимание на
фрагмент: class
Type, Type - это своего рода виртуальное имя типа которое будет
использоваться во всех функция этого класса. Теперь где-нибудь в коде
остается только создать объект: |
cArray< int > i_array; cArray< char > c_array; cArray< float > f_array;
|
Кстати, в самом
обыкновенном классе могут
быть шаблоны методов: |
class cArray{ public: template< class Type >void f( Type ){}; template< class Type > cArray( Type ); };
|
Теперь настала пора
сказать, что же
представляют из себя шаблоны на самом деле? Где-то внутри компилятора
это всего лишь макрос. Поэтому Вы должны всегда помнить следующую
особенность шаблонов: допустим у Вас в программе есть шаблон класса,
интерфейс которого занимает тысячу строк (например наш массив), как
только в примере выше компилятор встречает строчку cArray<int>
i_array он с радостью генерирует 1000 строк кода для типа данных int,
затем он видит cArray<char>
c_array и генерирует еще 1000 строк кода для типа char,
ну и на конец для cArray<float>
f_array компилятор с радостью сгенерирует ещё 1000 строк кода для
float’a. Таким образом мы практически ничего не сделали, а
уже получили увесистый exe’шник. Стоит сделать одну оговорку:
при данном коде компилятор сгенерирует код только для конструктора и
некоторых функций, вызываемых в нем. Вообще стоит отметить такую
особенность шаблонов: код для них генерируется только после
непосредственного использования шаблона в программе (будь то шаблон
класса или функции). Для классов это особенно удобно. Например, если в
нашем массиве есть функция Print(void){
++Array[0];} (не спрашивайте зачем она нам нужна), и мы будем хранить в
массиве объект, который нельзя инкрементировать, то до тех пор пока мы
не вызовем эту функцию проблем не будет. Компилятор просто не
сгенерирует для неё кода и проблемы с синтаксисом не возникнет. А вот
другой пример: |
template< class Type >void f( Type i ){ +*+*+*+*+*i+*+*+*+*+*; }
|
До тех пор пока мы не
будем пользоваться
этой функцией, компилятор будет молчать (он просто не будет знать, для
какого типа данных генерировать код). Он, конечно, проверит синтаксис
САМОГО ШАБЛОНА, ну что бы слово template писалось с двумя
“e” а не с десятью, но не более.
Помимо типов, шаблоны можно создавать, параметризуя их по какому-либо
конкретному значению, например: |
template< class Type , int n >class cArray{ public: Type Array[n]; //интерфейсные функции }; //инициализация cArray< int , 10 > i_array;//массив из 10 элементов
|
Можно создать шаблон
сразу по нескольким
типам: |
template< class Type1 , class Type2 >class cDArray{ public: cDArray(); void f(); }
template< class Type1 , class Type2 > cDArray< Type1 , Type2 >::cDArray(){ }
template< class Type1 , class Type2 >void cDArray< Type1 , Type2 >::f(){ }
|
И соответственно с
несколькими
параметрами: |
template< int N1 , int N2 >class cDArray{ public: float Array[ N1 ][ N2 ]; cDArray(); void f(){} }
template< int N1 , int N2 > cDArray< N1 , N2 >::cDArray(){ }
template< int N1 , int N2 >void cDArray< N1 , N2 >::f(){ }
|
Напоследок хочу
привести такой пример: |
for( int i( 0 ) ; i < 10 ; i++ ) cArray< int , i > i_array;
|
Он не будет работать.
Дело здесь в том,
что на этапе компиляции неизвестно количество итераций в цикле
(компилятору неизвестно, конечно же), соответственно компилятор не
знает для какого i генерировать код, поэтому он просто потребует
константного значения в угловых скобках.
Вот, пожалуй,
и все на сегодня. Надеюсь, шаблоны займут почетное место среди ваших
“инструментов”. Спасибо за внимание.
исходные
коды
Смежные вопросы:
Урок 1. Основы классов.
Урок 2. Конструкторы
копий, оператор присваивания.
Урок 3. Перегрузка
функций.
Урок 4. Друзья.
Урок 5. Перегрузка
операторов.
Урок 6. Наследование
(часть первая).
Урок 7. Наследование
(часть вторая). |