При закрытии подписчики были переданы в рассылку "Программирование на Visual С++" на которую и рекомендуем вам подписаться.
Вы можете найти рассылки сходной тематики в Каталоге рассылок.
Информационный Канал Subscribe.Ru |
C++ для всех. Выпуск 7 Шаблонные классы. Объявление шаблонных классов |
Здравствуйте, уважаемые подписчики
Думаю не ошибусь если скажу, что шаблоны и шаблонные классы в частности, являются самой горяче обсуждаемой темой в большинстве С++ журналов и конференций. С помощью шаблонов творят просто невообразимые вещи. Но об этом позже,- сейчас нас интересуют базовые понятия.
Как и шаблонные функции, шаблонные классы могут применятся для обработки разнотипных данных. Наибольшее применение получили в контейнерных классах, и как замечательный пример - дружно смотрим в сторону стандартной библиотеки STL.
Итак начнем. Сначала декларация:
template<class T> class A { public: A(T *p=0):ptype(p){} ~A(){ delete ptype; } void checkValue(const T&){} const T* getValue()const{ return ptype; } private: T *ptype; };
Теперь мы можем использовать вышеприведенный класс с любым типом:
struct STest{ int val; double x; }; int main() { A<int> a_int; A<int*> a_pointer_int; A<STest> *pa = new A<STest>(new STest()); ... delete pa; return 0; }
Шаблоны могут иметь несколько параметров, также в качестве параметров могут передаваться другие шаблоны. Для упрощения восприятия шаблонам можно давать эквивалентные имена с помощью ключевого слова typedef. Так, например широко используемый в стандартной STL тип std::string объявляется с помощью typedef:
namespace std{ typedef basic_string<char> string; typedef basic_string<wchar_t> wstring; }
Такая возможность позволяет значительно упростить внешний вид параметров. Вместо
a) int main() { A<std::basic_string<char> > a_int; return 0; }
можно использовать более изящное решение:
б) int main() { A<std::string> a_int; return 0; }
В примере a) прошу обратить внимание на способ записи параметров-шаблонов - между конечными угловыми скобками > > делается пробел, иначе будет ошибка компиляции. А теперь с несколькими параметрами:
template<class T1,class T2,class T3> class A { public: A(const T1&,const T2&,const T3&){} }; int main() { A<int,double,double*> a(10,20,new double(30)); return 0; }
Также шаблоны могут содержать уже определенные аргументы. Так поступают при необходимости получить определенное значение конкретного типа:
template<class T,int v> void func(T t) { std::cerr << "T: " << t << '\n'; std::cerr << "int: " << v << '\n'; } int main() { func<double,20>(100.23); return 0; }
Для шаблонов с определенными аргументами добавлю, что в эти аргументы не являются модифицируемыми, т.е. такие действия не пройдут:
template<class T,int v> void func(T t) { v++; //ошибка на этапе компиляции, - v не модифицирумый тип int x = &v; //аналогичная ошибка int &x = v; //аналогичная ошибка const int &x = v; //правильно };
Кроме того, для немодифицируемых аргументов условия сравнения нужно брать в скобки, т.к. первый встреченный '>' будет рассматриваться как конец объявления шаблона:
template<int i = 10>20> class A{}; //ошибка template<int i = (10>20)> class A{}; //нормально
Согласно стандарту (14.1.7) нельзя также объявлять шаблон с параметром-значением типа double, class или void, хотя некоторые компиляторы делают ограничения только на class:
struct STest{}; template<int d> class A; //нормально template<STest v> class B; //ошибка компиляции template<void d> class C; //ошибка компиляции template<void* d> class D; //нормально template<double d> class E; //ошибка компиляции template<double *d> class F; //нормально
Шаблонный тип может иметь тип-параметр по умолчанию. В ниже приведенном примере определение интерфейс переменных a и a1 равноценен:
template<class T = int> class A {}; int main() { A<> a; A<int> a1; return 0; }
Параметр-тип по умолчанию может быть либо последним либо все аргументы шаблона обязаны иметь определенные типы по умолчанию:
template<class T=int,class T2> class A{}; //ошибка компиляции - нет второго параметра по умолчанию template<class T,class T2=int> class B{}; //нормально template<class T=double,class T2=int> class C{}; //нормально
В то же время, возможно объявить шаблон за несколько заходов:
template<class T,class T2=double> class A; template<class T=int,class T2> class A;
Это равноценно такому объявлению:
template<class T=int,class T2=double> class A;
Причем объявление действует в контексте, пока не будет дополнено дополнительным объявлением, т.е.:
template<class T,class T2=double> class A; void func1() { A<> *a; //ошибка компиляции - отсутствует обязательный параметр A<int> *b; //нормально } template<class T=int,class T2> class A; void func2() { A<> *a; //нормально } template<class T,class T2> class A{};
Само собой разумеется, внутри шаблонного класса могут быть определены другие шаблонные классы (вложенные классы) и шаблонные функции:
template<class T> class A { public: template<class X> void anyFunc(const X &x){ A1<X> v; } protected: template<class T1> class A1 { protected: T1 *p_t1; T *p_t; }; }; int main() { A<double> a; return 0; }
Порядок шаблонных аргументов в объявлении и определении обязан совпадать:
template<class T1,class T2> class A { void func1(); void func2(); }; template<class T1,class T2> void A<T1,T2>::func1(){} //нормально template<class T1,class T2> void A<T2,T1>::func2(){} //ошибка компиляции
Имена шаблонных типов перекрывают глобальные типы поэтому для доступа к глобальным типам и переменным следует использовать оператор ::
class T{}; int val; template<class T,T val> void calcValue(T t) { T t1 = val; //здесь работают шаблонные параметры ::T t2 = ::val; //здесь глобальный тип и переменная }
Пожалуй, на сегодня хватит. Далее я приведу текст простого шаблонного класса списка указателей, - посмотрите на локальные классы и на способ объявления и определения функций. Что бы Вы могли изменить? Как и почему?
/**************************************************************************************** ptrlist.h - description ------------------- begin : Чтв Июл 24 2003 copyright : (C) 2003 by Yuri Gordienko email : iqsoft@cg.ukrtel.net *****************************************************************************************/ #include <stdlib.h> template<class type> class TPtrList { protected: struct SNode{ type *data; SNode *next; }; public: class Iterator{ friend class TPtrList; public: Iterator():curr(0){} Iterator(const Iterator &s):curr(s.curr){} bool isValid()const{ return curr!=0; } Iterator& operator++(){ if(curr) curr = curr->next; return *this; } const Iterator operator++(int){ Iterator tmp(element); ++*this; return tmp; } type* operator->()const{ return curr ? curr->data : 0; } type* operator*()const{ return curr ? curr->data : 0; } protected: Iterator(SNode *n):curr(n){} private: SNode *curr; }; TPtrList(bool autodelete=false):is_autodelete(autodelete),ifirst(0),icurr(0),count_i(0){} TPtrList(const TPtrList<type> &s):ifirst(0),icurr(0){} virtual ~TPtrList(){ clear(); } bool autoDelete()const{ return is_autodelete; } void setAutoDelete(bool ad){ is_autodelete=ad; } int count()const{ return count_i; } bool isEmpty()const{ return !ifirst; } void clear(); bool removeCurrent(){ return icurr ? remove(icurr->data) : false; } void append(type *d); bool remove(type *d); type* find(type *d)const; type* first(); type* last(); type* next(); Iterator begin()const{ return Iterator(ifirst); } Iterator end()const{ return Iterator(0); } const TPtrList& operator=(const TPtrList &s); private: SNode *ifirst,*ilast; SNode *icurr; int count_i; bool is_autodelete; }; template<class type> void TPtrList<type>::clear() { SNode *p; while(ifirst){ p = ifirst->next; if(is_autodelete) delete ifirst->data; free(ifirst); ifirst = p; } icurr = 0; count_i = 0; } template<class type> void TPtrList<type>::append(type *d) { if(ifirst){ ilast->next = (SNode*)malloc(sizeof(SNode)); ilast = ilast->next; } else{ ifirst = (SNode*)malloc(sizeof(SNode)); ilast = ifirst; } ilast->data = d; ilast->next = 0; ++count_i; } template<class type> bool TPtrList<type>::remove(type *d) { SNode *p = ifirst; SNode *p_old = 0; while(p){ if(p->data==d){ if(is_autodelete) delete p->data; if(p->next){ SNode *p1 = p->next; memcpy(p,p1,sizeof(SNode)); p = p1; } else{ if(icurr==p) icurr = 0; if(p_old) p_old->next = 0; else ifirst = 0; ilast = p_old; } free(p); --count_i; return true; } p_old = p; p = p->next; } return false; } template<class type> type* TPtrList<type>::find(type *d)const { SNode *p = ifirst; while(p){ if(p->data==d) return p->data; p = p->next; } return 0; } template<class type> inline type* TPtrList<type>::first() { icurr = ifirst; return icurr ? icurr->data : 0; } template<class type> inline type* TPtrList<type>::last() { icurr = ifirst ? ilast : 0; return icurr ? icurr->data : 0; } template<class type> inline type* TPtrList<type>::next() { if(icurr){ icurr = icurr->next; if(icurr) return icurr->data; } return 0; } template<class type> const TPtrList<type>& TPtrList<type>::operator=(const TPtrList<type> &s) { if(this!=&s){ if(ifirst) clear(); SNode *p = s.ifirst; ifirst = ilast = 0; while(p){ if(!ifirst){ ifirst = (SNode*)malloc(sizeof(SNode)); ilast = ifirst; } else{ ilast->next = (SNode*)malloc(sizeof(SNode)); ilast = ilast->next; } ilast->data = p->data; p = p->next; } if(ilast) ilast->next = 0; is_autodelete = false; count_i = s.count_i; } return *this; } int main() { TPtrList<int> vlist(true); //заполнили int i; for(i=0;i<10;++i) vlist.append(new int(i)); //обойдем и напечатаем через итератор TPtrList<int>::Iterator it; for(it=vlist.begin();it.isValid();++it) printf("%d\n",**it); //обойдем и изменим значение через функцию for(int *p=vlist.first();p;p=vlist.next()) *p = i--; //обойдем и напечатаем через итератор for(it=vlist.begin();it.isValid();++it) printf("%d\n",**it); return 0; }
На сегодня все. Пожелания, предложения, вопросы прошу на iqsoft@cg.ukrtel.net
В следующем выпуске мы подробно рассмотрим этот пример и детально поговорим о вложенных шаблонных классах, итераторах и шаблонных friend-классах.
(с) Юрий Гордиенко
http://subscribe.ru/
E-mail: ask@subscribe.ru |
Отписаться
Убрать рекламу |
В избранное | ||