专栏:C/C++
个人主页:HaiFan.
专栏简介:本章为大家带来C++的模板(初阶)。
面向对象编程(OOP)和泛型编程都能处理在编写程序时不知道类型的情况。不同之处在于:OOP能处理类型在程序运行之前都未知的情况;而在泛型编程中,在编译就能获知类型了。
模板是泛型编程的基础。一个模板就是一个创建类或者函数的蓝图或者说是公式。当使用一个vector这样的泛型类型,或者find这样的泛型函数时,我们提供足够的信息,将蓝图转换为特定的类或者函数。
泛型编程:编写与类型无关的通用代码,是代码复用的一种手段。模板是泛型编程的基础 。
现在编写一个比较两个数大小的函数,事实上,我们要定义多个这样的函数才可以完成每一种内置类型的比较。
int max(const int& a, const int& b)
{
return a > b ? a : b;
}
double max(const double& a, const double& b)
{
return a > b ? a : b;
}
使用函数重载固然可以实现,但是也有不足:
上面的代码中,两个函数只有参数不同,内容相同。
C++给出了解决办法—模板
可以定义一个通用的函数模板,而不是为每一个类型都定义一个函数。一个函数模板就相当于一个蓝图,根据这个蓝图生成符合类型的函数。
模板的定义是以关键字 template
开始,后跟一个模板参数列表,参数列表以逗号进行分割,用 <>
这两个符号包起来
在模板定义中,模板参数列表不能为空
#include
using std::cout;
using std::endl;
//int max(const int& a, const int& b)
//{
// return a > b ? a : b;
//}
//
//double max(const double& a, const double& b)
//{
// return a > b ? a : b;
//}
template //or template ,也可以用class,但是不能用struct
T max(const T& a, const T& b)
{
return a > b ? a : b;
}
int main()
{
int a = 1, b = 2;
double a1 = 1.1, b1 = 2.2;
cout << max(a, b) << endl;
cout << max(a1, b1) << endl;
return 0;
}
输出结果 2 2.2
在编译器编译阶段,对于模板函数的使用,编译器需要根据传入的实参类型来推演生成对应类型的函数以供调用,比如double类型使用函数模板时,编译器通过对实参类型的推演,将T确定为double类型,然后产生一份专门处理double类型的代码。
编译器在调用函数模板的时候,会自动的根据实参类型来推断模板中的参数类型。
cout << max(1, 2) << endl;
比如这个,实参类型是int,编译器会推断出模板实参为int。
用不同类型的参数使用函数模板时,称为函数模板的实例化。模板参数实例化称为显式实例化和隐式实例化。
template
T Sub(const T& a, const T& b)
{
return a - b;
}
int main()
{
int a1 = 2, b1 = 1;
double a2 = 2.2, b2 = 3.3;
Sub(a1, b1);
Sub(a2, b2);
return 0;
}
在使用这个函数模板的时候,编译器会自动的推导出类型,这个过程叫做隐式实例化。
template
T Sub(const T& a, const T& b)
{
return a - b;
}
int main()
{
int a1 = 2, b1 = 1;
double a2 = 2.2, b2 = 3.3;
Sub(a1, b2);
return 0;
}
上面的代码中,模板参数列表只有一个T,在实例化的过程中,需要推导实参类型,通过a1会将T推导为int,而b2会将T推导为double类型,这个时候,参数列表中的T无法确定是int还是double。
此时就需要用户自己强转
Sub(a1, (int)b2);
或者使用显式实例化(在函数名后面的<>中指定模板参数的实际类型)
Sub(a1, b2);
template
class 类模板名
{
// 类内成员定义
};
template
class Stack
{
public:
Stack(size_t capacity = 4)
:_capacity(capacity)
, _t(-1)
,a(new T[capacity])
{}
~Stack();//当用模板的时候,在类中声明,在类外定义是如何写代码的?
void Push(T x)
{
a[++t] = x;
}
void pop()
{
--t;
}
private:
T* a;
int _capacity;
int _t;
};
//类模板中函数放在类外进行定义时,需要加模板参数列表。
template
Stack::~Stack()
{
if (a)
{
delete[] a;
}
}
类模板的实例化与函数模板不同,类模板的实例化需要在类模板名字后跟 <>
,然后将实例化的类型放在 <>
中,类模板名字不是真正的类,而是实例化的结果才是真正的类。
int main()
{
Stack stk;
Stack stk1;
return 0;
}