20 июля 2011 в 19:36
Шаблоны (templates): шаблонные классы и функции в C++
Собрался я было писать текст про всякие крутые структуры данных и тут оказалось, что мы ещё не разбирали несколько очень важных возможностей C++. Шаблоны - одна из них.
Шаблоны (templates) - очень мощное средство. Шаблонные функции и классы позволяют очень сильно упростить программисту жизнь и сберечь огромное количество времени, сил и нервов. Если вам покажется, что шаблоны не сильно-то и значимая тема для изучения, знайте - вы заблуждаетесь.
Шаблонные функции
Простой пример шаблонной функции:
Type square (Type a) { Type b; b = a*a; return b; } int x = 5; int i; i = square(5); float y = 0.5; float f; f = square(y);
Если бы мы создавали функции по старинке, то тогда бы пришлось писать две разные функции: для типа int и для типа float. А если бы понадобилась такая же функция, использующая другие типы, пришлось бы заново писать и её. Используя шаблоны, можно ограничиться только одним экземпляром функции, оставив всю грязную работу компилятору.
Вместо использования какого-то определённого типа, в функции используется параметрический тип (или по другому - аргумент шаблона). Здесь я обозвал параметрический тип идентификатором Type. В функции этот идентификатор встречается три раза: возвращаемое значение, аргумент функции и определение переменной s. То есть Type используется как любой обычный тип.
Но чтобы код заработал, перед функцией нужно добавить следующую строку (я показал несколько вариантов синтаксиса, все они рабочие):
template <class Type> Type square (Type a) template <class Type> Type square (Type a) template< class Type > Type square (Type a) template < class Type > Type square (Type a)
Итак, перед функцией должно стоять ключевое слово template (шаблон), а в угловых скобках нужно указать имя параметрического типа с ключевым словом class. Вместо ключевого слова class можно использовать type - в общем-то никакой разницы.
Идентификатор параметрического типа тоже может быть любым. Мы часто будем пользоваться вот такими: TypeA, TypeB, Datatype, T.
Важное замечание: У шаблонных функций должен быть аргумент, чтобы компилятор мог определить какой именно тип использовать.
В шаблонах можно использовать несколько параметрических типов, и конечно же можно смешивать параметрические типы со стандартными (только нужно позаботиться о правильном приведении типов). Приведу пример в котором используется два параметрических типа TypeA, TypeB и базовый тип int:
template <class TypeA, class TypeB> TypeB example_function (TypeA a, TypeB b) { int x = 5; b = a + x; return b; }
Но шаблонные функции - не самое интересное, что мы сегодня рассмотрим.
Шаблонные классы
В общем-то шаблонные классы создаются почти так же как и шаблонные функции - перед именем класса записывается ключевое слово template. Шаблонные классы рассмотрим на примере стека:
template <class Type> class stack { private: int top; Type s[10]; public: stack (): top(0) {} void push(Type var) { top++; s[top] = var; } Type pop(); }; template <class Type> Type stack::pop() { Type var = s[top]; top--; return var; } Здесь мы опре
делили стек из десяти элементов. Эти элементы могут быть какого угодно типа, об этом чуть-чуть ниже.
Единственное на что хочу обратить ваше внимание: определение функций push и pop. Функция push определена внутри класса, а функция pop - снаружи. Для всех функции объявлённых за пределами класса, нужно обязательно указывать ключевое слово template. Выражение перед именем функции совпадает с тем, которое указывается перед именем класса.
Теперь посмотрим как работать с шаблонными классами:
stack<int> s1; stack<float> s2; s1.push(3); s1.push(2); s1.pop(); s2.push(0.5);
При создании объекта, после имени класса нужно поставить угловые скобки, в которых указать нужный тип. После этого объекты используются так, как мы привыкли.
У шаблонных классов есть одна потрясающая особенность - кроме стандартных типов, они могут работать и с пользовательскими. Рассмотрим небольшой пример. Для этого определим простой класс warrior:
class warrior { public: int health; warrior () : health(0) {} }; stack<warrior> s; warrior w1; warrior w2; warrior w3; s.push(w1); s.push(w3); s.pop(); s.push(w2);
Смотрите, теперь в стеках можно размещать переменные типа warrior!!! Возможно вы не поверите мне, но это очень круто! Насколько это круто, вы сможете убедиться когда на основе списков мы будем создавать графы и деревья.
По шаблонам пока всё. Позже разберём более сложные случаи использования шаблонных классов.
комментарии отсутствуют
авторизуйтесь