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;
}

编译器编译阶段,编译器根据传入的实参类型来推演生成对应类型的函数以供调用。
这时候就又要提一个概念了,,,就是函数模版的实例化。

用不同类型的参数使用函数模板时,称为函数模板的实例化。

模板参数实例化分为: 隐式实例化和显式实例化。
隐式实例化:让编译器根据实参推演模板参数的实际类型
显式实例化:在函数名后的<>中指定模板参数的实际类型

注:成员函数模板只有在被调用时才会被实例化。

C++之模版上篇
C++之模版上篇

模板参数的匹配原则

  1. 一个非模板函数可以和一个同名的函数模板同时存在,而且该函数模板还可以被实例化为这个非模板函数
  2. 对于非模板函数和同名函数模板,如果其他条件都相同,在调动时会优先调用非模板函数而不会从该模板产生出一个实例。如果模板可以产生一个具有更好匹配的函数,那么将选择模板
  3. 模板函数不允许自动类型转换,但普通函数可以进行自动类型转换

类模版

类模版的格式:
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之后的参数类型名称,而什么是非类型形参?顾名思义,就是表示一个固定类型的常量而不是一个类型。

非类型形参需要注意的是:

  1. 浮点数不可以作为非类型形参,包括float,double。
  2. 类不可以作为非类型形参。
  3. 字符串不可以作为非类型形参
  4. 整形,可转化为整形的类型都可以作为形参,比如int,char,long,unsigned,bool,short(enum声明的内部数据可以作为实参传递给int,但是一般不能当形参)
  5. 指向对象或函数的指针与引用(左值引用)可以作为形参
    非类型的模板参数必须在编译期就能确认结果。
//举个栗子
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;
}