前面有说过C++的另一特点就是泛型编程,接下来介绍的模板就是泛型中很重要的概念
一、函数模板
1、若两个函数内部逻辑完全相同,只有变量类型不同,如下:
void func(int i, int j){};
void func(double i, double j){};
可以用函数模板解决:
template<class 参数类型1,class 参数类型2,........>
返回值类型 模板名(形参表)
{
函数体
}
例:
template <class T1,class T2>
T1 func(T1 i,T2 j)
{
函数体;
}
template <class T>
T func2(T a)
{
函数体;
}
注:一个template声明只声明一个函数或者类为模板
模板可以通过参数的类型来实例化模板,也可不通过参数实例化模板
template<class T>
T func(T i, T j)
{
return i + j;
}
int main(void)
{
int i = 2,j = 2;
std:;cout<<func(i,j)<<endl;
std::cout<<func<double>(i,j)/2<<endl;
return 0;
}
注:模板一定会经历实例化的过程
2、函数模板的重载
函数模板可以重载,只要形参表或者类型参数表不同即可
template<class T>
T func(T i,T j){};
template<class T1, class T2>
T1 func(T1 i,T2 j){};
template<class T>
T func(T i){};
3、函数模板和函数的次序
在有多个函数和函数模板名字相同的情况下,编译器如下处理一条函数调用语句
(1)、先找参数完全匹配的普通函数
(2)、再找参数完全匹配的模板函数
(3)、再找实参通过类型强制转换后能够匹配的普通函数
(4)、都找不到error
二、类模板:为了很方便的定义出一批相似的类,可以定义类模板,然后由类模板定义出一批不同的类
在定义类的时候,加上一个/多个类型参数。在使用类模板时,指定类型参数应该如何替换成具体类型,编译器据此生成相应的模板类。
1、类模板的定义
template <class 类型参数1,class 类型参数2>
template <typename 类型参数1,typename 类型参数2>
class 类模板名
{
成员
};
在定义类模板时,class与typename一样,它们的区别后面会说
2、定义类模板的成员函数的写法
template 类模板名<类型参数列表名>
返回值类型 类模板名<类型参数名列表>::成员函数名(参数表)
{
函数体
}
与普通类的成员函数定义的区别就是在类名后加了声明类型参数名列表
用类模板定义对象的写法:
类模板名<真实类型参数表> 对象名(构造函数实参表)
demo:pair类
template <class T1,class T2>
class Pair
{
public:
T1 key;
T2 value;
Pair (T1 k,T2 v):key(k),value(v){};
bool operator < (const Pair<T1,T2> & p ) const;
};
template <class T1,class T2>
bool Pair<T1,T2>::operator < (const Pair<T1,T2> & p) const
{
return key < p.key;
}
有类模板生成类的过程叫类模板的实例化。由类模板实例化得到的类,叫做模板类(真绕口。。。)
同一个类模板的两个模板类不兼容(就是两个不同的类)
3、函数模板可以作为类模板的成员
template <class T>
class example
{
public:
template<class T2>
void func(T2 i){};
};
4、类模板的<类型参数表>中可以出现非类型参数
template <class T,int size>
class example
{
private:
T array[size];
};
example<int, 10> a;
example<double ,20> b;
类a,b属于不同类
三、类模板与派生:
类模板从类模板派生:在派生类时要用当前类的类型参数去实例化基类的类型参数
类模板从模板类派生
类模板从普通类派生:所有从类模板实例的类都是以普通类为基类
普通类从模板类派生