C++模板
什么是模板?
模板时C++支持参数化多态的工具,它可以实现类型参数化,即把类型定义为参数。真正实现了代码的可重用性,减轻了编程及维护的工作量和难度。
模板是类或函数可在编译时定义所需处理和返回的数据类型,一个模板并非一个实实在在的类或函数。仅仅时一个类或函数的描述。模板一般分成类模板和函数模板。
模板是泛型编程的基础,泛型编程即独立与任何特定类型的方式编写代码。
模板是创建泛型类或函数的蓝图或公式。库容器,比如迭代器的算法,都是泛型编程的基础,它们都使用了模板的概念。
每一个容器都有单一的定义,比如向量我们可以定义不同类型的向量,比如vector 或 vector.
泛型:泛型编程即一种独立与任何特定类型的的方式编写代码。
函数模板
为什么要引入函数模板呢?
int max(int a,int b){return a>b?a:b}
float max(float a,float b){return a>b?a:b}
double max(double a,double b){return a>b?a:b}
例如这三个函数中,除了类型不同,其他函数逻辑完全相同。
这时,我们会想,可不可以把类型作为参数呢?
这就是函数模板的作用。
函数模板的一般形式:
template <typename T>
函数头
{
函数体
}
实例:
#include <iostream>
using namespace std;
template <typename T>
T max(T a,T b)
{
return a > b ? a : b;
}
int main()
{
cout << max<int>(1, 2) << endl;
return 0;
}
这是函数模板的定义:
template <typename T>
T max(T a,T b)
{
return a > b ? a : b;
}
当模板被调用时,编译器生成的函数叫做模板函数。
在计算机中,如果我们仅仅写出了函数模板,而没有使用它,那么计算机是不会产生任何代码数据的,因为它也不知道要产生什么代码数据。也就是说函数模板并不是一个函数。
在定义函数模板时,我们可以使用class代替typename,这两种可以混用。但是一般的,我们使用typename定义模板,而用class定义类。
变量作为模板参数
代码实例:
template <int size>
void display()
{
cout << size << endl;
}
display<10>();
多参数函数模板
代码实例:
template <typename T,typename C>
void display(T a,C b)
{
cout << a << "," << b << endl;
}
display<int, double>(1, 1.88);
函数模板与重载(包含非类型模板参数)
其实,函数模板就相当于是一种重载函数,因为由一个模板就可以派生出许多函数。
不同的函数模板所生成的函数也能够形成重载。
template <typename T>
void display(T a);
template <typename T>
void display(T a, T b);
template <typename T,int size>
void display(T a);
display<int>(10);
display<int>(5,10);
display<int,5>(30);
inline函数模板
函数模板与非模板函数一样的方式声明为inline。说明符放在模板形参表之后,返回类型之前。不能放在template之前。
OK
template <typename T> inline
函数头
{
函数体
}
error
template <typename T> inline
函数头
{
函数体
}
实例化
所谓实例化,就是用类型参数去代替模板中的模板参数,生成一个具体类型的真正函数。
实例化可分为隐式实例化和显式实例化。
隐式实例化
隐式实例化是根据函数调用时所传入的数据类型来确定模板中模板参数的类型,模板参数的类型时隐式确定的。
例如:
add(1,2);
函数调用时不传入类型参数,编译器会根据所传入实参的数据类型来隐式确定模板参数的类型。
显式实例化
隐式实例化不能为同一个模板指定两种不同的类型。可以用另一种实例化的方式来解决在这个问题——显式实例化,显式实例化就是显式的指定函数模板中的数据类型。
显式实例化是人为的写入模板参数的值,比如:
add(1,2);
需要注意的是:如果写入的模板参数与传入的实参类型不匹配时,会发生强制类型转换。
类模板
既然说函数有模板,那么在C++中类有没有模板呢?那答案是肯定的。
为什么会有类模板呢?因为我们有时使用类的时候发现,一个类有的时候会用到多次,只有类型不同。这时就要用到类模板了。
泛型类一般声明:
template <class T>
class class-name
{
}
template <class T>
class MyArray
{
public:
void display()
{...}
private:
T* m_pArr;
};
我们在类内定义成员函数时,与一般类的定义没有什么区别,但是在类外定义成员函数时,我们必须这样:
template <class T> //模板的形参表列,即类模板定义的第一行
void MyArray<T>::display() //模板的形参名,不带有任何类型,只是名字
{
...
}
类模板的实例化
使用类模板时,必须在类名后写上<形参名表列> 。
类模板必须实例化为模板类,在通过模板类实例化为对象
A //类模板
A //模板类,即类名
与函数模板相同,类模板本身没有实质性的代码,只有在使用模板时,在会自动生成代码。
分离编译
特别注意:模板代码不能分离编译。
也就是说你不能写成.h文件和.cpp文件分开。
类模板实例:
#include <iostream>
using namespace std;
/**
* 定义一个矩形类模板Rect
* 成员函数:calcArea()、calePerimeter()
* 数据成员:m_length、m_height
*/
template <typename T>
class Rect
{
public:
Rect(T length,T height);
T calcArea();
T calePerimeter();
public:
T m_length;
T m_height;
};
template <typename T>
Rect<T>::Rect(T length, T height)
{
m_length = length;
m_height = height;
}
template <typename T>
T Rect<T>::calcArea()
{
return m_length * m_height;
}
template <typename T>
T Rect<T>::calePerimeter()
{
return (m_length + m_height) * 2;
}
int main(void)
{
Rect<int> rect(3, 6);
cout << rect.calcArea() << endl;
cout << rect.calePerimeter() << endl;
return 0;
}