模板
定义:
模板就是实现代码重用机制的一种工具, 它可以实现类型参数化, 即把类型定义为参数, 从而实现了真正的代码可重用性。
作用:
模板用于表达逻辑结构相同, 但具体数据元素类型不同的数据对象的通用行为。
一、函数模板
1、函数模板:建立一个通用函数,其返回值类型和形参类型不具体指定,用一个虚拟的类型来代替。(编译器并不是将函数模板处理成能够处理任意类型的函数,而是通过调用函数模板的具体类型产生不同的函数)
函数模板是不允许隐式类型转换的,调用时类型必须严格匹配
声明类型:
template <类型形式参数表(class/typename)>
返回类型 函数名 (形参列表)
{
函数体
}
#include <iostream>
using namespace std;
/*
void myswap(int &a, int &b)
{
int t = a;
a = b;
b = t;
}
void myswap(char &a, char &b)
{
char t = a;
a = b;
b = t;
}
*/
//template 关键字告诉C++编译器 我要开始泛型了.你不要随便报错
//数据类型T 参数化数据类型
template <typename T>
void myswap(T &a, T &b)
{
T t;
t = a;
a = b;
b = t;
}
int main()
{
//char a = 'c';
int x = 1;
int y = 2;
myswap(x, y); //自动数据类型 推导的方式
float a = 2.0;
float b = 3.0;
myswap(a, b); //自动数据类型 推导的方式
myswap<float>(a, b); //显示类型调用
cout<<"hello..."<<endl;
return ;
}
2、模板函数
当函数模板被调用时, 编译器会生成相应的模板函数。
模板函数和普通函数一起时的调用规则:
1 、函数模板可以向普通函数一样被重载
2、C++编译器优先考虑普通函数
3、如果函数木板可以产生一个更好的匹配(普通函数会发生参数类型的强制转换),那么选择模板
3、可以通过空模板实参列表( 模板函数名<>(参数列表) )的语法限定编译器只能通过模板匹配。
4、函数模板不允许自动类型转换(参数类型转换),普通函数能够发生自动类型转换。
3、模板的实例化
例:bool Compare<int>(int a,int b) (模板+类型)
4、模板的实参推演
限制:1、不能产生二义性;
2、有实参。
① 依次检查每个函数实参以确定在每个函数参数的类型中出现的模板参数
② 如果找到模板参数则通过检查函数实参的类型推演出相应的模板实参
③ 函数参数类型和函数实参类型不必完全匹配下列类型转换可以被应用在函数实参上以便将其转换成相应的函数参数的类型
④如果在多个函数参数中找到同一个模板参数则从每个相应函数实参推演出的模板实参必须相同
5、模板的特例化
①完全特例化(全特化)char*:较大部分函数不能某种类的逻辑;
函数模板 类模板
②部分特例化(偏特化)T*:较小部分函数不能满足某种类型的逻辑;(部分函数进行特例化(必须有模板))
类模板
特例化一个函数模板时,必须为原模板中的每个模板参数都提供实参。使用关键字template后跟一个空尖括号<>,即template <>
,以指出我们正在特例化一个模板
注意:特例化版本满足模板版本的逻辑。
6、类型参数
7、非类型参数
①常量;
②不允许浮点型;
8、模板的重载
①普通函数版本
bool Compare(char *cpa,char *cpb)
{
return strcmp(cpa,cpb)>0;
}
②模板的版本
template<typename T>
bool Compare(T a,T b)
{
return a >b;
}
③模板特例化版本
template<>
bool Compare(char *a,char *b)
{
return strcmp(a,b)>0;
}
9、模板的默认值 (传递+推演=>实例化)
①函数模板的默认值
a.取传递的值;
b.推演;
c.默认值。
②类模板的默认值
10、模板来接收不明确类型的返回
11、显示实例化
typename 用法:
1、定义模板类型参数;
2、指明模板中的类型。
二、类模板
定义:与函数模板的定义类似。 建立一个通用类,用来处理功能相同, 但数据类型不同的多个类
类模板中的拷贝构造函数的模板(使用时,必须指定类型)会退化成构造函数。
类中没拷贝构造函数系统调用默认的拷贝构造函数。
建议:构造函数和析构函数可以缺省类型参数、其他地方不要缺省。
区分类模板与模板类的概念
一个类模板(类生成类)允许用户为类定义个一种模式,使得类中的某些数据成员、默认成员函数的参数,某些成员函数的返回值,能够取任意类型(包括系统预定义的和用户自定义的)。
如果一个类中的数据成员的数据类型不能确定,或者是某个成员函数的参数或返回值的类型不能确定,就必须将此类声明为模板,它的存在不是代表一个具体的、实际的类,而是代表一类类。
模板类是类模板实例化后的一个产物,说个具体点的例子吧,我们把类模板比作是一个做饼干的模子,而模板类就是用这个模子做出来的饼干,至于这个饼干是什么味道的就要看你自己在实例化时用的是什么材料了,你可以做巧克力饼干,也可以做牛奶饼干,这些饼干出了材料不一样外,其它的东西都是一样的了。
声明类型:
template <类型形式参数表(class/typename)>//template<typename T>
class 类名
{
类的数据成员和成员方法
}
#include <iostream>
template<typename T>
class CLink
{
public:
template<typename E>
friend class CLink;
public:
CLink()
{
phead = new Node();
}
~CLink()
{
Node* pCur = phead;
Node* pBack = pCur;
while (pCur != NULL)
{
pBack = pCur->pnext;
delete pCur;
pCur = pBack;
}
phead = NULL;
}
//CLink(const CLink<T>& rhs);
template<typename E>
CLink(const CLink<E>& rhs)
{
phead = new Node();
Node* pNewCur = phead;
CLink<E>::Node* pCur = rhs.phead->pnext;
while (pCur != NULL)
{
Node* pnewnode = new Node(pCur->mdata);
pNewCur->pnext = pnewnode;
pCur = pCur->pnext;
pNewCur = pNewCur->pnext;
}
}
void push_front(const T& val)
{
Node* pnewnode = new Node(val);
pnewnode->pnext = phead->pnext;
phead->pnext = pnewnode;
}
void Show()
{
Node* pCur = phead->pnext;
while (pCur != NULL)
{
std::cout << pCur->mdata << " ";
pCur = pCur->pnext;
}
std::cout << std::endl;
}
class Node;
Node* find(const T& val)
{
Node* pCur = phead->pnext;
while (pCur != NULL)
{
if (pCur->mdata == val)
{
return pCur;
}
pCur = pCur->pnext;
}
return NULL;
}
private:
class Node
{
public:
Node(T val = T()) :mdata(val), pnext(NULL){};
public:
T mdata;
Node* pnext;
};
Node* phead;
};
int main()
{
CLink<int> ilink;
CLink<double> dlink(ilink);
CLink<int> ilink2(ilink);
return 0;
}
在类模板外部定义成员函数时, 需要加上模板说明,在类名后面加上<T>
//在类模板外部定义成员函数时, 需要加上模板说明,在类名后面加上<T>
template <typename T>
void CLink<T>::Insert(T val)
{
Node <T>* pnode = new Node<T>(val);
pnewnode->pnext = phead->pnext;
phead->pnext = onewnode;
}
template <typename T >
Node <T>*CLink<T>::Find(T val)
{
Node <T> pCur = phead;
while(pCur->pnext != NULL)
{
if(pCur->pnext->value == val)
{
return pCur;
}
pCur = pCur->pnext;
}
return NULL;
}
template<typename T>
void CLink<T>::Delete(T val)
{
Node<T>* front = Find(val);
if(pfront != NULL)
{
Node<T>* pCur = pfront->pnext;
pfrong->pnext = pCur->pnext;
delete pCur;
}
}
类型的实例化是在访问限定符检查之后进行;
类模板的优点:
1、它是类型无关的,具有高度的可复用性
2、它在编译时而不是运行时检查数据类型,保证了类型安全。
3、与平台无关,可移植性。
函数模板与类模板的区别:函数模板的实例化是由编译程序在处理函数调用时自动完成的;而类模板的实例化必须由程序员在程序中显式地指定。