【c++】模板总结

模板:模板的使用是为了适应泛型编程,泛型编程就是编写与类型无关的代码,是代码复用的一种手段,模板就是实现泛型编程的基础

模板函数

1.概念

模板函数代表了一个函数家族,该模板函数与类想无关,在使用时被参数化,根据实参产生函数的特定类型版本。

2.模板函数的格式:

class =typename !=struct(尽量使用typename更明显)

template<typename  T1,typename  T2...>

返回值类型  函数名(参数列表){}

template <class T>
bool IsEqual(const T& a,const T& b)
{
	return a==b;
}

注意:模板函数也可以定义为内联函数,inline关键字必须放在模板形参之后,返回值之前,不能放在template之前

template <class T>
inline T add(const T& left,const T& right)
{
	return left+right;
}

3.模板函数实例化&参数推演

模板的本质相当于一张图纸,它本身不是类或者函数,编译器用模板产生指定的类或者函数的特定类型版本,产生类型的过程称为函数模板实例化

注意:函数模板参数的编译分为两个阶段

扫描二维码关注公众号,回复: 2331304 查看本文章
  • 实例化之前,检查模板代码本身,是否出现简单语法错误,如:模板参数列表少了typename
  • 在实例化后,检查模板生成的代码,是否所有的调用都有效,如:实例化类型不支某些函数调用

从函数实参确定模板形参类型和值的过程称为模板实参推演,多个类型形参的实参必须完全匹配

#include <iostream>
#include <stdlib.h>
using namespace std;

template <class T>
bool IsEqual(const T& a,const T& b)
{
	return a==b;
}


template <class T>
bool IsEqual(const int& a,const int& b)
{
	return a==b;
}

template <class T1,class T2>
bool IsEqual(const T1& a,const T2& b)
{
	return a==b;
}

int main()
{
	int a=10;
	int b=20;
	char c='a';
	char d='c';
	//编译器调用函数模板时,编译器会根据传递的参数自动推演出模板形参的类型,并
	//自动生成对应的代码
	cout<<IsEqual(a,b)<<endl;
	cout<<IsEqual(c,d)<<endl;
	cout<<IsEqual(a,c)<<endl;

	return 0;
}

对于已经实现了的模板函数。自动调用,不会重复推演

cout<<IsEqual<int>(a,b)<<endl;//int int 已经实现了模板函数,自动调用,不会推演
cout<<IsEqual<char>(c,d)<<endl;//char char
cout<<IsEqual(a,c)<<endl;//int char

4.模板函数的形参

类型形参

  • 模板形参名字只能在模板形参之后到模板声明或定义的末尾之间使用,遵循名字屏蔽原则
  • 模板形参的名字在同一模板形参列表中只能使用一次
  • 所有模板形参前面必须加上class或者typename关键字修饰

非类型形参

非模板类型形参是模板内部定义的常量,在需要常量表达式的时候,可以使用非模板类型参数

template<class T,size_t N>
void PrintArray(const T (&array)[N])//数组引用
{
	for(size_t i=0;i<N;i++)
	{
		cout<<array[i]<<" ";
	}
	cout<<endl;
}

int main()
{
	const int count=9;
	int a[10]={0,1,2,3,4,5,6,7,8,9};
	int b[count+1]={1,2,3,4,5,6,7,8,9,0};
	int c[9]={1,2,3,4,5,6,7,8,9};
	PrintArray(a);
	PrintArray(b);
	PrintArray(c);
	return 0;
}

模板形参表说明:

  1. 模板形参表使用<>括起来
  2. 和函数参数表一样,跟多个参数时必须用逗号隔开,类型可以相同也可以不同
  3. 定义模板函数时模板形参不能为空
  4. 模板形参可以是类型形参,也可以是非类型形参,类型形参在class和typename后
  5. 模板类型形参可作为类型说明符用在模板中的任何地方,与内置类型或自定义类型使用方法完全相同,可用于指定函数形参类型,返回值,局部变量和强制类型转换
  6. 模板形参中,class和typename具有相同的含义,可以互换,使用typename更加直观,但关键字typename是作为c++标准加入到c++中的,旧的编译器可能不支持

函数模板重载

int max(const int& left,const int& right )
{
	return (left>right)?left:right;
}


template<class T>
T max(const T& left,const T& right)
{
	return (left>right)?left:right;
}

template<class T>
T max(const T& left,const T& mid,const T& right)
{
	return max(max(left,mid),right);
}

int main()
{
	int a=10;
	int b=20;
	int m=90;
	
	cout<<max(a,b,m)<<endl;
		return 0;
}

注意:模板的所有重载版本的声明必须都应该位于该函数模板被调用位置之前

说明:

  • 一个非模板函数可以和一个同名的函数模板同时存在,而且该函数模板还可以被实例化成这个非模板函数
  • 对于非模板函数和同名函数,如果其他条件都相同,在调用时会优先调用非模板函数,而不会从该模板产生出一个实例,如果模板可以产生一个局域更好匹配的函数,那么将选择模板
  • 显示定义一个空的模板实参列表,该语法会告诉编译器只有模板才能来匹配这个调用,而且所有的模板参数都应该根据实参演绎出来
  • 模板函数不允许自动类型转换,但普通函数可以进行自动类型转换

上面的代码如果调用字符类型的比较,那么它返回的结果是错误的,该如何解决呢

函数模板特化

template<class T>
T Max(T left, T right)
{
	return left > right? left : right;
}


template<>
char* Max<char*>(const char* left, char* right)
{
	if(strcmp(left, right) > 0)
		return left;

	return right;
};


int main()
{
	char* p1 = "hello";
	char* p2 = "world";
	cout<<Max(p1, p2)<<endl;
	return 0;
}

函数模板特化形式如下:

  • 必须有一个基础的函数模板
  • 关键字template后面接一队空的<>
  • 函数名后接模板和和一对<>,尖括号中指定这个特化定义的模板形参
  • 函数形参表:必须和模板函数的基础参数类型相同
  • 函数体

注意:在特化的模板版本调用中,实参类型必须与特化版本的函数形参保持一致,如果不匹配,编译器将为实参模板定义中实例化一个实例

类模板

类名:AA  Vector

类型名:AA  Vector<T>

类模板的格式

template <class 形参名,class 形参名,...>
class 类名
{}

顺序表类

template <class T>
class Seqlist
{
public:
	Seqlist()
		:_size(0)
		,_capacity(10)
		,_arr(new T[_capacity])
	{}
	~Seqlist()
	{
		delete []_arr;
		_arr=NULL;
	}
protected:
	size_t _size;
	size_t _capacity;
	T* _arr;
};

void TestSeqlist()
{
	Seqlist<int> s1;
	Seqlist<double> s2;
}

非类型形参:非模板类型形参是模板内部定义的常量,在需要常量表达式的时候,可以使用非模板类型参数。如在静态顺序表中,需要自定义大小的顺序表

注意:浮点数和类对象是不允许作为非类型模板参数的

关于模板的分离编译

main.c

int main()
{
	SeqList<int> s;

	return 0;
}

SeqList.h

template <class T>
class SeqList
{
public:
	SeqList();
protected:
	size_t _size;
	size_t _capacity;
	T* _arr;
};

SeqList.cpp

template<class T>

SeqList<T>::SeqList()
	:_size(0)
	,_capacity(0)
	,_arr(0)
{}

解决办法:

  • 在模板头文件 xxx.h 里面显示实例化->模板类的定义后面添加 template class SeqList<int>; 一般不推荐这种方法,一方面老编译器可能不支持,另一方面实例化依赖调用者(不推荐)
  • 将声明和定义放到一个文件 “xxx.hpp” 里面,推荐使用这种方法

模板优点:

  • 模板复用了代码,节省资源,更快的迭代开发,C++的标准模板库 (STL)因此而产生
  • 增强了代码的灵活性

缺点:

  • 模板让代码变得凌乱复杂,不易维护,编译代码时间变长
  • 出现模板编译错误时,错误信息非常凌乱,不易定位错误 

猜你喜欢

转载自blog.csdn.net/lw__sunshine/article/details/81155103
今日推荐