C++之模版上篇
首先,我们要讲模版,先要提到泛型编程。要问泛型编程是啥,那我们先看看官方的解释。
一种语言机制,能够帮助实现一个通用的标准容器库。泛型编程让你编写完全一般化并可重复使用的算法,其效率与针对某特定数据类型而设计的算法相同。泛型即是指具有在多种数据类型上皆可操作的含义,与模板有些相似。
通俗点来说,就是编写与类型无关的通用代码,把数据类型作为一种参数传递进来,从而实现代码的复用。
模版一般分为函数模版和类模版。
函数模版
函数模版的格式:
template<typename T1, typename T2,…,typename Tn>
返回值类型 函数名(参数列表) { }
这里需要注意的一点是typename是用来定义模板参数关键字,也可以使用class(但是不能使用struct代替class)
template<typename T> //在下次调用函数的时候,传入类型
void Swap(T &a, T &b)
{
T tmp = a;
a = b;
b = tmp;
}
编译器编译阶段,编译器根据传入的实参类型来推演生成对应类型的函数以供调用。
这时候就又要提一个概念了,,,就是函数模版的实例化。
用不同类型的参数使用函数模板时,称为函数模板的实例化。
模板参数实例化分为: 隐式实例化和显式实例化。
隐式实例化:让编译器根据实参推演模板参数的实际类型
显式实例化:在函数名后的<>中指定模板参数的实际类型
注:成员函数模板只有在被调用时才会被实例化。
模板参数的匹配原则
- 一个非模板函数可以和一个同名的函数模板同时存在,而且该函数模板还可以被实例化为这个非模板函数
- 对于非模板函数和同名函数模板,如果其他条件都相同,在调动时会优先调用非模板函数而不会从该模板产生出一个实例。如果模板可以产生一个具有更好匹配的函数,那么将选择模板
- 模板函数不允许自动类型转换,但普通函数可以进行自动类型转换
类模版
类模版的格式:
template<class T1, class T2, …, class Tn>
class 类模板名
{
// 类内成员定义
};
template<class T>
class A
{
public:
A(T a = 10)
:_a(a)
{
cout<< "构造函数 "<<endl;
}
~A();
private:
T _a;
};
//注意:类模板中函数放在类外进行定义时,需要加模板参数列表
A <int>::~A()
{
cout << "析构函数 " << endl;
}
类模板实例化需要在类模板名字后跟<>,然后将实例化的类型放在<> 中即可,要记住实例化的结果才是真正的类。A是类名,A< int >是类型
int main()
{
A<int> s;
return 0;
}
刚刚说数据类型是作为一种参数传递进来,而模板参数则分为类型形参与非类型形参。
类型形参: 出现在模板参数列表中,跟在class或者typename之后的参数类型名称,而什么是非类型形参?顾名思义,就是表示一个固定类型的常量而不是一个类型。
非类型形参需要注意的是:
- 浮点数不可以作为非类型形参,包括float,double。
- 类不可以作为非类型形参。
- 字符串不可以作为非类型形参
- 整形,可转化为整形的类型都可以作为形参,比如int,char,long,unsigned,bool,short(enum声明的内部数据可以作为实参传递给int,但是一般不能当形参)
- 指向对象或函数的指针与引用(左值引用)可以作为形参
非类型的模板参数必须在编译期就能确认结果。
//举个栗子
template<class T, int MAXSIZE>
class A
{
public:
void Print()
{
cout << "The maxsize is " << MAXSIZE << endl;
}
private:
T a[MAXSIZE];
};
int main()
{
A<int, 5> s;
s.Print();//打印"The maxsize is 10"
system("pause");
return 0;
}