Шаблоны в С++ Макаревич Л. Г.
Шаблоны функций Многие алгоритмы не зависят от типов данных. Многие алгоритмы не зависят от типов данных. #include using namespace std; void swap (int& a, int & b) { int t = a; a = b; b = t; } void swap (double& a, double & b) { double t = a; a = b; b = t; } void swap (char& a, char& b) { char t = a; a = b; b = t; } int main() { int i = 5, j = 6; double d = , b = ; char c1='a', c2 = 'f'; swap (i, j); swap (d, b); swap (c1, c2); }
template void swap_templ (T& a, T & b) { T t = a; a = b; b = t; } int main() { int i = 5, j = 6; double d = , b = ; char c1='a', c2 = 'f'; swap_templ(i,j); swap_templ(d,b); swap_templ(c1,c2); } template void f( ) { …} template void f( T& a, int k) { a = a + k; } int main() { int i = 5; f ( i, 99 ); //можно f (i,99); //будет выполнено преобразование в //double return 0; }
template const T& min(const T& a, const T& b) { if (a < b) return a; else return b; } Данная запись еще не создала ни одной функции, это лишь шаблон для определенной функции. Только тогда, когда происходит обращение к функции с аргументами конкретного типа, будет выполнена генерация конкретной функции. int x, y, z; String s1, s2, s3;... // генерация функции min для класса String s1 = min(s2, s3);... // генерация функции min для типа int x = min(y, z); double d; x = min(y,d);// сообщение об ошибке Первое обращение к функции min генерирует функцию const String& min(const String& a, const String& b); Второе обращение генерирует функцию const int& min(const int& a, const int& b);
Explicit specification of the template arguments for a function template is allowed. For example: // function_templates2.cpp template void f(T) { } int myfunction(char j) { f (j); //generate the specialization f(int) } When the template argument is explicitly specified, normal implicit conversions are done to convert the function argument to the type of the corresponding function template parameters. In the above example, the compiler will convert (char j) to type int. Явное указание аргумента шаблона при вызове template void f(T k) {cout
template void f(int c, int k) { T& a; a = a + k + c; } int main() { int i = 5; f ( i, 99 ); // ошибка return 0; } В списке параметров шаблона должны быть только классы, входящие в число входных аргументов функции В списке параметров шаблона должны быть только классы, входящие в число входных аргументов функции Тип возвращаемого значения может входить в список параметров шаблона, если такой тип есть во входных аргументах функции Тип возвращаемого значения может входить в список параметров шаблона, если такой тип есть во входных аргументах функции template T f(int c, int k) { T a = 0; a = a + k + c; return a; } int main() { int i = 5; int k = f ( i, 99 );// ошибка return 0; }
#include using namespace std; template void sort(T * a, int n)// сортировка выбором { T t; for ( int i = 0; i < n -1; i++ ) { int imin = i; for ( int j = i + 1; j < n; j++) if ( a[j] < a[imin]) imin = j; t = a[i]; a[i] = a[imin]; a[imin] = t; } int main() { const int n = 5; int b[n]; for ( int i = 0; i > b[i]; sort(b,n); double d[] = {22, 5, 67, 4}; sort(d,4); return 0; }
Специализированные шаблоны template void sort(T * a, int n) { T t; for ( int i = 0; i < n -1; i++ ) { int imin = i; for ( int j = i + 1; j < n; j++) if ( a[j] < a[imin]) imin = j; t = a[i]; a[i] = a[imin]; a[imin] = t; } void sort(int * a, int n) { cout b[i]; sort(b,n); double d[] = {22, 5, 67, 4}; sort(d,4); return 0; }
With a function template, you can define special behavior for a specific type by providing a explicit specialization (override) of the function template for that type. Visual C and later supports the new syntax for declaring explicit specializations of function templates. For example: // explicit_specialization.cpp template void f(T t) { }; // Explicit specialization of f with 'char' with the // template argument explicitly specified: template void f (char c) { } // Explicit specialization of f with 'double' with the // template argument deduced: template void f(double d) { } int main() { } The following form of old syntax is also supported: // Explicit specialization of f with 'char' with the // template argument deduced: // void f(char) {...} Дополнительные возможности задания специализированных шаблонов
Шаблоны классов Представьте контейнерный класс ( список, очередь, дерево, стек, …) Представьте контейнерный класс ( список, очередь, дерево, стек, …) Контейнерный класс предназначен для хранения данных Контейнерный класс предназначен для хранения данных
class List { class Elem { public: int data; Elem * next; Elem * prev; Elem(){data = 0; next = prev = NULL;} Elem( int d):data(d){next = prev = NULL;} }; Elem * first; Elem * last; public: List(void) { first = last = NULL; } ~List(void) { Elem * pt = first; while ( pt ) { Elem * t = pt; pt = pt->next; delete t; } Elem * insert( int key, int d)// вставка после узла с ключом key { if (Elem * pkey = find(key)) { Elem * pt = new Elem(d); pt->next = pkey->next; pt->prev = pkey; pkey->next = pt; if ( pkey != last ) (pt->next)->prev = pt; else last = pt; return pt; } return 0; }
void Add( int d)// вставка в конец списка { Elem * t = new Elem(d); if ( first == NULL ) { first = last = t; } else { t->prev = last; last->next = t; last = t; } void print() { Elem * pt = first; while ( pt ) { cout data next; } void print_reverse() { Elem * pt = last; while ( pt ) { cout data prev; } Elem * find ( int d ) { Elem * pt = first; while ( pt ) { if ( pt->data == d ) return pt; pt = pt->next; } return NULL; }
bool remove( int key ) { if (Elem * pkey = find(key)) { if ( pkey == first ) { first = first->next; first->prev = NULL; } else { if ( pkey == last ) { last = last ->prev; last->next = NULL; } else { (pkey->prev)->next = pkey->next; (pkey->next)->prev = pkey->prev; } delete pkey; return true; } return false; } }; int main() { List l; for ( int i = 1; i < 25; i++ ) l.Add(i); l.print(); l.print_reverse(); l.insert(1,99); if ( !l.remove(6)) cout
template имя_класса { тело класса }
template class List { class Elem { public: T data; Elem * next; Elem * prev; Elem(){data = 0; next = prev = NULL;} Elem( T d):data(d){next = prev = NULL;} }; Elem * first; Elem * last; public: List(void) { first = last = NULL; } ~List(void) { Elem * pt = first; while ( pt ) { Elem * t = pt; pt = pt->next; delete t; } Elem * insert( T key, T d)// вставка после узла с ключом //key { if (Elem * pkey = find(key)) { Elem * pt = new Elem(d); pt->next = pkey->next; pt->prev = pkey; pkey->next = pt; if ( pkey != last ) (pt->next)->prev = pt; else last = pt; return pt; } return 0; }
void Add( T d)// вставка в конец списка { Elem * t = new Elem(d); if ( first == NULL ) { first = last = t; } else { t->prev = last; last->next = t; last = t; } void print() { Elem * pt = first; while ( pt ) { cout data next; } void print_reverse() { Elem * pt = last; while ( pt ) { cout data prev; } Elem * find ( T d ) { Elem * pt = first; while ( pt ) { if ( pt->data == d ) return pt; pt = pt->next; } return NULL; }
bool remove(T key ) { if (Elem * pkey = find(key)) { if ( pkey == first ) { first = first->next; first->prev = NULL; } else { if ( pkey == last ) { last = last ->prev; last->next = NULL; } else { (pkey->prev)->next = pkey->next; (pkey->next)->prev = pkey->prev; } delete pkey; return true; } return false; } }; int main() { List l; for ( int i = 1; i < 5; i++ ) l.Add(i); l.print(); l.print_reverse(); l.insert(1,99); if ( !l.remove(4)) cout
Использование итераторов #define NULL 0 #include using namespace std; template class List { class Elem { public: T data; Elem * next; Elem * prev; public: Elem(){data = 0; next = prev = NULL;} Elem( T d):data(d){next = prev = NULL;} }; Elem * first; Elem * last; public: List(void) { first = last = NULL; } ~List(void) { Elem * pt = first; while ( pt ) { Elem * t = pt; pt = pt->next; delete t; } void Add( T d)// вставка в конец списка { Elem * t = new Elem(d); if ( first == NULL ) { first = last = t; } else { t->prev = last; last->next = t; last = t; } void print() { Elem * pt = first; while ( pt ) { cout data next; } public: class Iterator { List * p; Elem * tek; public: Iterator(List *_p):p(_p){tek = p->first;} T gettek(){return tek->data;} bool next() { if ( tek == NULL ) return false; tek = tek->next; if ( tek != NULL) return true; return false; } void setfirst(){ tek = p->first;} }; void main() { List qq; for ( int i = 0; i < 10; i++) qq.Add(i); List ::Iterator it(&qq); for ( i = 0; i < 5; i++) { cout
You can use class templates to create a family of classes that operate on a type. // class_templates.cpp template class TempClass { public: TempClass( void ); ~TempClass( void ); int MemberSet( T a, int b ); private: T Tarray[i]; int arraysize; }; int main() { TempClass ma; } In this example, the templated class uses two parameters, a type T and an int i. The T parameter can be passed any type, including structures and classes. The i parameter has to be passed an integer constant. Because i is a constant defined at compile time, you can define a member array of size i using a standard array declaration.
Члены шаблонов template class TempClass { int MemberSet(T, int); }; template int TempClass ::MemberSet( T a, int b ) { if( ( b >= 0 ) && (b < i) ) { Tarray[b++] = a; return sizeof( a ); } else return -1; } template тип_возврата имя_класса :: имя_функции ( список параметров функции) { // тело функции }
В шаблоне можно использовать шаблонные функции, но функция должна быть описана в шаблоне. template class X { public: template void mf(const U &u) { } }; int main() { } Так нельзя: template class X { public: template void mf(const U &u); }; template template void X ::mf(const U &u) { } int main() { }
Конструкторы и деструкторы в шаблонах template class TempClass { TempClass(); ~TempClass(); }; template TempClass ::TempClass( void ) { TRACE( "TempClass created.\n" ); } template TempClass ::~TempClass( void ) { TRACE( "TempClass destroyed.\n" ); } int main() { }
template class MyStack { T* pStack; T StackBuffer[i]; static const int cItems = i * sizeof(T); public: MyStack( void ); void push( const T item ); T& pop( void ); }; template MyStack ::MyStack( void ) { }; template void MyStack ::push( const T item ) { }; template T& MyStack ::pop( void ) { }; int main() { }
template class MyStack { T* pStack; T StackBuffer[i]; public: static const int cItems = i * sizeof(T); MyStack( void ); void push( const T item ); T pop( void ); }; template MyStack ::MyStack( void ) { pStack = &StackBuffer[0]; }; template void MyStack ::push( const T item ) { *pStack = item; pStack++; }; template T MyStack ::pop( void ) { --pStack; return *pStack; }; int main() { MyStack mys; for ( int i = 0; i < 6; i++) mys.push(i); for ( i = 0; i < 6; i++) cout
MyStack stack1; // creates a stack of // unsigned longs MyStack stack2; // uses code created above MyStack stack3; // generates new code MyStack stack4; // generates stack of //MyClass objects
Специализация шаблона класса: Специализация отдельного методаСпециализация отдельного метода Специализация классаСпециализация класса template void List ::print();// общий метод void List ::print();// специализированный метод template class Block { }; class Block { // повторяется все тело класса };
template class MyString { }; MyString ms; MyString ms_char; template class My { int x; }; template class My // специализация шаблона { long x; }; template class V> class C // параметр шаблона – // шаблон со специализацией { V y; V z; }; C c;
Использование параметра шаблона и параметра конструктора template class MyStack { T* pStack; int tek; public: MyStack( void ){tek = 0; pStack = new T[i];} void push( const T item ){pStack[tek++]=item;} T pop( void ){return pStack[--i];} }; template class MyStack { T* pStack; int tek; public: MyStack( int k = 50 ){tek = 0; pStack = new T[k];} void push( const T item ){pStack[tek++]=item;} T pop( void ){return pStack[--i];} };
template class X // или class { public: template void mf(const U &u) {U i = u; } }; int main() { X x1; x1.mf( 55); return 0; }
Правила создания шаблонов Шаблон должен лежать в h-файле Шаблон должен лежать в h-файле Отдавайте предпочтение шаблонам перед производными классами, если важно время выполнения Отдавайте предпочтение шаблонам перед производными классами, если важно время выполнения Отдавайте предпочтение производным классам перед шаблонами, если важно добавление нового кода без перекомпиляции Отдавайте предпочтение производным классам перед шаблонами, если важно добавление нового кода без перекомпиляции Отдавайте предпочтение шаблонам перед производными классами, если нельзя выделить общий базовый класс Отдавайте предпочтение шаблонам перед производными классами, если нельзя выделить общий базовый класс Шаблоны – полиморфизм времени компиляции или параметрический полиморфизм Шаблоны – полиморфизм времени компиляции или параметрический полиморфизм На шаблонах основаны библиотеки контейнерных классов, например, STL На шаблонах основаны библиотеки контейнерных классов, например, STL