为什么要有模板?
·使用函数重载,针对每个具有相同行为的不同类型重新实现
int Add(int &x, int &y)
{
return x + y;
}
float Add(float &x, float &y)
{
return x + y;
}
缺点:
1、只要有新类型出现,就要重新添加对应函数
2、除类型外,所有函数的函数体都相同,代码的复用率不高
3、如果函数只是返回值类型不同,函数重载不能解决
4、一个方法有问题,所有的方法都有问题,不好维护
·使用公共基类,将通用的代码放在公共的基础类里面
缺点:
1、借助公共基类来编写通用代码,将失去类型检查的优点
2、对于以后实现的许多类,都必须继承自某个特定的基类,代码维护更加困难
·使用特殊处理的预处理程序
#define Add(a,b) ((a)+(b))
缺点:
不是函数,不进行参数类型检测,安全性不高
模板:是泛型编程的基础。所谓的泛型编程就是编写与类型无关的逻辑代码,是一种复用的方式。
模板分为:模板函数和模板类
一、模板函数
模板函数的格式:
template<class 形参名1,class 形参名2,...,class 形参名n>
返回值类型 函数名(参数列表)
{...}
模板形参的定义可以使用class,也可以使用typename
1、模板函数的实例化
template<class T>
bool IsEqual(const T& left, const T& right)
{
return left == right;
}
int main()
{
string s1("111");
string s2("222");
cout << IsEqual(s1, s2) << endl;
cout << IsEqual(1, 2) << endl;
}
从汇编层看
2、模板参数匹配及显示实例化
template<class T>
bool IsEqual(const T& left, const T& right)
{
return left == right;
}
int main()
{
string s1("111");
string s2("222");
//cout << IsEqual(1, 1.2) << endl;//模板实参不匹配
cout << IsEqual<string>(s1, s2) << endl;//显示实例化
cout << IsEqual<int>(1, 2) << endl;//显示实例化
}
有现成的就调用现成的
指定数据类型就调用模板
现成的与模板实例化后的函数构成重载
3、重载函数模板
bool IsEqual(const int& left, const int& right)
{
return left == right;
}
template<class T>
bool IsEqual(const T& left, const T& right)
{
return left == right;
}
template<class T1,class T2>
bool IsEqual(const T1& left, const T2& right)
{
return left == right;
}
int main()
{
cout << IsEqual(1, 1) << endl;
cout << IsEqual<int>(1, 1) << endl;
cout << IsEqual(1, 1.2) << endl;
}
二、模板类
类模板的格式:
template<class 形参名1,class 形参名2,....,class 形参名n>
class 类名
{...};
1、模板类
//动态顺序表
template<class T>
class Vector
{
public:
Vector();
~Vector();
private:
int _size;
int _capacity;
T* _data;
};
template<class T>
Vector<T>::Vector()
:_size(0)
, _capacity(10)
, _data(new T[_capacity])
{}
template<class T>
Vector<T>::~Vector()
{
delete[] _data;
}
int main()
{
Vector<int> sl1;
Vector<double> sl2;
}
2、模板参数——实现容器适配器
#include<iostream>
using namespace std;
template<class T>
class Vector
{
private:
int _size;
int _capacity;
T* _data;
};
//template<class T,class Container>
template<class T,class Container=Vector<T>>
class Stack
{
public:
void Push(const T& x);
void Pop();
const T& Top();
bool Empty();
private:
Container _con;
};
int main()
{
Stack<int> s1;
Stack<int, Vector<int>> s2;
}
3、模板的模板参数——容器适配器
template<class T>
class Vector
{
private:
int _size;
int _capacity;
T* _data;
};
template<class T,template<class>class Container>
class Stack
{
public:
void Push(const T& x);
void Pop();
const T& Top();
bool Empty();
private:
Container _con;
};
int main()
{
Stack<int> s1;
Stack<int, Vector> s2;
}
三、模板的参数
1、函数模板
函数模板的类型参数分为:模板参数和调用参数
模板形参分为:类型形参和非类型形参
(1)类型形参
模板形参名字只能在模板形参之后到模板声明或定义的末尾之间使用,遵循名字屏蔽规则
模板形参名字在同一模板形参列表中只能使用一次
template<class T,class T>
void f(T t1, Tt2)
{}
所有模板形参前面必须加上class或typename关键字
注意:在函数模板的内部不能指定缺省的模板参数
(2)非模板类型参数
非类型模板参数是模板内部定义的常量,在需要常量表达式的时候使用
2、非类型的类模板参数
//静态顺序表
//template<class T,size_t MAX>
template<class T,size_t MAX=10>//带缺省模板参数
class SeqList
{
public:
SeqList();
private:
T _array[MAX];
int _size;
};
void Test()
{
SeqList<int> s1;
SeqList<int, 20> s2;
}
int main()
{
Test();
return 0;
}
注意:浮点数和类对象是不允许作为非类型模板参数的
模板形参说明:
a、模板形参表使用<>括起来
b、和函数参数表一样,跟多个参数时必须用逗号隔开,类型可以相同也可以不同
c、定义模板函数时,模板形参表不能为空
d、模板形参可以作为类型说明符用在模板中的任何地方,与内置类型或自定义类型使用方法完全相同,可用于指定函数形参类型、返回值、局部变量和强制类型转换
e、模板形参列表中,class和typename具有相同意义,可以相互交换,使用typename更加直观。但旧的编译器不支持
四、模板的特化
1、模板函数特化
可以这样来定义:
template<>
int compare<const char*>(const char* const p1, const char* const p2)
{
return strcmp(p1, p2);
}
模板函数特化形式:
a、关键字class或typename后面跟<>
b、函数名后接模板名和一对尖括号,尖括号内指定这个特化定义的模板形参
c、函数形参表
d、函数体
template<>
返回值 函数名<Type>(参数列表)
{
//函数体
}
特化声明必须与特定的模板相匹配
2、类模板的特化
全特化
#include<iostream>
using namespace std;
template<class T>
class SeqList
{
public:
SeqList();
~SeqList();
private:
int _size;
int _capacity;
T* _data;
};
template<class T>
SeqList<T>::SeqList()
:_size(0)
, _capacity(0)
, _data(new T[_capacity])
{
cout << "SeqList()" << endl;
}
template<class T>
SeqList<T>::~SeqList()
{
delete[] _data;
}
template<>
class SeqList<int>
{
public:
SeqList(int capacity);
~SeqList();
private:
int _size;
int _capacity;
int* _data;
};
//特化后定义成员函数不再需要模板形参
SeqList<int>::SeqList(int capacity)
:_size(0)
, _capacity(capacity)
, _data(new int[_capacity])
{
cout << "SeqList<int>()" << endl;
}
//特化后定义成员函数不再需要模板形参
SeqList<int>::~SeqList()
{
delete[] _data;
}
void Test()
{
SeqList<double> s1;
SeqList<int> s2(2);
}
int main()
{
Test();
system("pause");
return 0;
}
偏特化
#include<iostream>
using namespace std;
template<class T1,class T2>
class Data
{
public:
Data();
private:
T1 _d1;
T2 _d2;
};
template<class T1,class T2>
Data<T1, T2>::Data()
{
cout << "Data<T1,T2>" << endl;
}
//局部特化第二个参数
template<class T1>
class Data<T1,int>
{
public:
Data();
private:
T1 _d1;
int _d2;
};
template<class T1>
Data<T1, int>::Data()
{
cout << "Data<T1,int>" << endl;
}
//局部特化两个参数为指针类型
template<class T1, class T2>
class Data<T1*,T2*>
{
public:
Data();
private:
T1 _d1;
T2 _d2;
T1* _d3;
T2* _d4;
};
template<class T1, class T2>
Data<T1*, T2*>::Data()
{
cout << "Data<T1*,T2*>" << endl;
}
//局部特化两个参数为引用
template<class T1, class T2>
class Data<T1&, T2&>
{
public:
Data(const T1& d1,const T2& d2);
private:
const T1& _d1;
const T2& _d2;
T1* _d3;
T2* _d4;
};
template<class T1, class T2>
Data<T1&, T2&>::Data(const T1& d1,const T2& d2)
:_d1(d1)
, _d2(d2)
{
cout << "Data<T1&,T2&>" << endl;
}
void Test()
{
Data<double, int> d1;
Data<int, double> d2;
Data<int*, int*> d3;
Data<int&, int&> d4(1, 2);
}
int main()
{
Test();
system("pause");
return 0;
}
模板的全特化和偏特化都是在已定义的模板基础之上,不能单独存在
五、模板的分离编译
1、什么叫模板的分离编译?
声明和定义分开
2、模板为什么编不过分离编译?
模板不支持分离编译