概述
模板是泛型编程的基础面向对象编程是运行时多态,而泛型编程是编译时多态
模板是在编译时编译器可以自动实例化的
函数模板
template <class/typename parameter ,class/typename parameter ,...>//关键字class比较老,typename后来加入C++//因为相对来讲template是不怎么常用的编程,所以<>这对尖括号会写错,每一个class/typename可能也会忘记写
returnValue function(parameter);
inline函数模板
template <class/typename parameter ,class/typename parameter ,...>inline returnValue function(parameter);
类模板
- <span style="font-size:14px;">eg:
- template <class _Tp, class _Alloc = __STL_DEFAULT_ALLOCATOR(_Tp) >
- class list : protected _List_base<_Tp, _Alloc> {</span>
类模板的使用
要显式指定模板实参- <span style="font-size:14px;">eg:
- list<string></span>
模板形参
模板形参与变量名差不多- <span style="font-size:14px;">eg:
- template <class _Tp, class _Alloc = __STL_DEFAULT_ALLOCATOR(_Tp) >//在同一形参表中,形参名不能相同,不能都为_Tp,不然没有办法区分
- class vector : protected _Vector_base<_Tp, _Alloc>
- {
- //形参的作用域和一般类或者一般函数作用域相同,都为template和类体或者函数体(类外定义部分当然也算)
- }
- //模板可只声明,再定义(声明和定义中的形参不一定要相同,和一般函数形参相似)</span>
模板类型形参
形参为类类型
eg:
template <class _Tp, class _Allocator>class _Vector_alloc_base<_Tp, _Allocator, true> {
public:
typedef typename _Alloc_traits<_Tp, _Allocator>::allocator_type
allocator_type;
非模板类型形参
形参不一定为类类型
- <span style="font-size:14px;">eg:
- template <class _Tp,size_t N>
- void printArray(_Tp (&array)[N])
- {
- for(size_t i = 0; i != N; ++i)
- {
- cout<<array[i]<<'\t';
- }
- }</span>
编写模板代码时,应该尽量减少对实参的要求
实例化
类/函数模板的实例化都是在使用时才会实例化
模板实参推断
- <span style="font-size:14px;">eg:
- Test<int> iT;//类模板形参<int>是必须的,Test<int>才是一个类型
- compare(1,0);//这里叫做模板实参推断,就是从函数实参推断出模板实参类型和值的过程</span>
如果是模板形参是非引用类型,则数组实参将转换成指向数组第一个元素的指针,同样函数实参将转换成指向函数类型的指针
- <span style="font-size:14px;">//模板形参为非引用类型
- eg:
- template <typename T> T fobj(T,T);
- int a[10],b[10];
- fobj(a,b);//转换为 fobj(int *,int *)</span>
- <span style="font-size:14px;">//模板形参为引用类型
- eg:
- template <typename T> T fref(const &T,const &T);
- int a[10],b[10];
- fref(a,b);//不能转换 形参为引用时,数组不能转换为指针</span>
- <span style="font-size:14px;">eg:
- #include <cstring>
- template<class Value_Type>
- class HashTable
- {
- public:
- HashTable(bool (*compare_ptr)(const Value_Type obj1, const Value_Type obj2), unsigned long size = 100):
- isEqual(compare_ptr) {}
- private:
- bool (*isEqual)(const Value_Type obj1, const Value_Type obj2);
- };
- bool _compare(const char* val1, const char* val2)
- {
- if (strcmp(val1, val2) == 0)
- return true;
- else
- return false;
- }
- int main()
- {
- HashTable<const char* > hash(_compare);
- }</span>
函数模板的显式实参
- <span style="font-size:14px;">eg:
- template <class t1,class t2,class t3>
- t1 sum(t1,t2);//如果返回值放第一位,可以如下使用
- sum<long>(123L,456L);
- sum(static_cast<int>(s),i);//模板形参不一致导致返回值类型不定时,可以用static_cast<type>来强制转换实参</span>
模板编译模型
很多编译器都不支持模板声明和定义分离1.包含编译模型
一般应该把声明和定义都放在.h中or
在声明之后 #include "实现.cpp"
如果实现是一组内联函数,可把内联函数放在.inl文件中,在.h的末尾加上 #include "xxx.inl"
2.分别编译模型
export关键字//.h
声明模板
//.cpp
#include <.h>
export 定义模板
//如果是类的话,还可以不一定export 类,可以只export某些类成员
因为编译器支持的比较少,这种separate编译就不着重介绍了
类模板成员
在类外声明类模板成员template <class T> 类名<T>::模板成员函数(类名 cp);
类模板中的友元声明
1.普通友元
直接声明就行friend class ClassName;
friend retType function(args);
2.一般模板友元 一对多的关系
template <class Type>class A
{
template <class T> friend class ClassName;//类模板友元
template <class T> friend retType tempFun(const T&);//函数模板友元
}
A的每一个实例,ClassName和tempFun的所有实例都是友元
3.特定模板友元 一对一的关系
- eg:
- //前向声明
- template <class T> class ClassName;
- template <class T> retType tempFun(const T&);
- //特定友元
- template <class T>
- class A
- {
- friend class ClassName<char *>;
- friend retType tempFun<char *>(char *const &);
- };
- //前向声明
- template <class T> class ClassName;
- template <class T> retType tempFun(const T&);
- //特定友元
- template <class Type>
- class A
- {
- friend class ClassName< Type >;
- friend retType tempFun< Type >(const Type &);
- };
- eg:
- A<int> 与 ClassName<int> & tempFun<int>是友元
关于operator<<重载的小问题
//declaretemplate <class _Tp> class Queue;
template <class _Tp>
std::ostream& operator<<(std::ostream &os,const Queue<_Tp> &ref);
template <class _Tp>
class QueueItem
{
friend class Queue<_Tp>;
friend std::ostream& operator<< <_Tp> (std::ostream&, const Queue<_Tp>&ref);//这里的<_Tp>使编译发现重载的友元运算符是一个模板
所谓的从形参推断是指在真正调用函数模板时,通过函数形参推断模板形参,这样才推断出是普通的友元函数还是友元函数模板
成员模板
任意类(模板类/普通类)都可以拥有类模板或函数模板成员,就是成员模板,成员模板不能为虚,成员模板遵循常规访问控制模板类在外部定义成员模板
- eg:
- template <class T> template <class Iter>//有两个模板形参表
- void Queue<T>::assign(Iter beg,Iter end)
- {
- }
类模板的static成员
类模板的每一个类型的实例化共享同一static成员- eg:
- template <class T>
- class A
- {
- public:
- A(){}
- static std::size_t getCount()
- {
- return cnt;
- }
- private:
- static std::size_t cnt;
- };
- template <class T>//与普通static成员不一样,要说明是template的
- std::size_t A<T>::cnt=0;
- //使用
- A<int> i1,i2,i3;//i1,i2,i3都共享一个cnt;
- A<string> s1;//s1独自共享另一个cnt;
模板特化
函数模板特化
函数模板只能全特化,无法偏特化,只能重载函数模板特化之前一般函数模板必须要存在
- eg:
- //.h
- template <class _Tp>
- int myCompare(const _Tp &s1,const _Tp &s2)//一般函数模板
- {
- if (s1<s2)
- {
- return -1;
- }
- else if (s2<s1)
- {
- return 1;
- }
- else//s1==s2
- {
- return 0;
- }
- }
- template<>
- int myCompare<const char*>(const char* const &s1,const char* const &s2) //全特化模板
- {
- return strcmp(s1,s2);
- }
- //.cpp
- //使用·
- const char *ch1="hello";
- const char *ch2="world";
- cout<<myCompare(ch2,ch1);//return 1
类模板特化
类模板可以全特化、偏特化也可以只特化某些类成员函数//全特化
- eg:
- //.h
- template <>
- class A<const char*>
- {
- public:
- void function(const char*);
- };
- //.h中的定义
- template<>
- void A<const char*>::function(const char*)
- {
- //code here
- }
- eg:
- //一般类模板
- template <class T1,class T2>
- class A
- {
- };
- //偏特化
- template <class T1>
- class A<T1,int>
- {
- };
- eg:
- //特化声明
- template <>
- void Queue<const char*>::push(const char* const &);
- template <>
- void Queue<const char*>::pop();
- //特化定义
- template <>
- void Queue<const char*>::push(const char* const &val)
- {
- char *newChar=new char[strlen(val)+1];
- strncpy(newChar,val,strlen(val)+1);
- QueueItem<const char*> *pt=new QueueItem<const char*>(newChar);
- if(empty())
- {
- head=tail=pt;
- }
- else
- {
- tail->next=pt;
- tail=pt;
- }
- }
- template <>
- void Queue<const char*>::pop()
- {
- QueueItem<const char*> *tempForDelete=head;
- delete head->data;
- head=head->next;
- delete tempForDelete;
- }