从零开始学c++————模板初阶

Posted sjp11

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了从零开始学c++————模板初阶相关的知识,希望对你有一定的参考价值。

在这里插入图片描述

一.模板函数

如何实现一个通用的的两个数相加的函数呢?

我们可以通过函数重载把各个类型的两个数相加给写出来,如下:

int Add(int& A, int& B)//int类型的相加函数
{
	return A + B;
}

double Add(double& A, double& B)//double类型的相加函数
{
	return A + B;
}

char Add(char& A, char& B)//char类型的相加函数
{
	return A + B;
}


。。。。

使用函数重载虽然可以实现,但是以下缺点:

  1. 重载的函数代码的复用率比较低,每种类型的函数都需要再写一份

  2. 代码的可维护性比较低,一个出错可能所有的重载均出错,修改时每份代码都需要进行修改,很麻烦

然而c++中的模板函数就能解决以上缺点,下面该代码包含了所有类型的两个数相加的函数

template<class A>//T是模板参数
A  Add(A& x1, A& x2)/
{
    return x1+x2;
}

1.模板函数的概念:

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

模板函数的定义:

template<class T1, classT2,......,class Tn> 
返回值类型 函数名(参数列表){}

其中class是用来定义模板参数关键字,也可以使用typename,都一样的,不过我个人较喜欢用class,下面我会用class来写模板,T1,T2,Tn等都是模板参数的名字,你也可以任意取名字都无所谓。

函数模板的原理:

template<class T>
void  Swap(T& x1, T& x2)//交换类型的模板函数
{
	T tmp = x1;
	x1 = x2;
	x2 = tmp;
}

int main()
{
    //下面两个例子是调用Swap函数的地方是同一个函数还是两个函数
    //转到反汇编,看调用的函数的地址是否一样
    int a = 4, b = 5;
	Swap(a, b);

	double c = 1.2, d = 2.3;
	Swap(c, d);
}

在这里插入图片描述

可以看到调用double和int型的Swap的函数的地址不一样,所以调用的函数不是同一个函数。
所以,函数模板的推演,通过实参推出形参的类型T是double,int,char,然后调用相对应的函数。
在这里插入图片描述

在编译器编译阶段,对于模板函数的使用,编译器需要根据传入的实参类型来推演生成对应类型的函数以供调用。比如:当用int类型参数使用函数模板时,编译器通过对实参类型的推演,将T确定为int类型,然后去调用专门处理int类型函数,该过程由计算机帮你完成,对于其它类型也是如此

模板实例化:

用不同类型的参数使用函数模板时,称为函数模板的实例化。模板参数实例化分为:隐式实例化和显式实例化。

隐式实例化:不指定模板参数的类型,让编译器自动去推出模板参数的类型;

显示实例化:在函数名后的<>中指定模板参数的实际类型,然后编译器根据类型去调用相对应的函数

template <class T>
T Add(T& A, T& B)
{
	return A+ B;
}

int main()
{
	int a = 10, b = 20;
	cout<<Add(a, b)<<endl;//隐式实例化,编译器会推导出T为int

	double c = 2.00, d = 3.00;
	cout<<Add(c, d)<<endl;隐式实例化,编译器会推导出T为double

    Add(a,c)//该语句编译不过,因为在编译期间,编译器推导出a的类型为int,c的类型为double,但模板参数只有一个T
    //编译器无法知到T具体是哪种类型,所以编译不过

   Add(a,(int)c);//该语句编译的过,把c强制类型转换为int型

   Add<int>(a,c);//该语句编译的过,显示实例化,不需要编译器去推导,直接按int类型去相加

	return 0;
}

2.模板的匹配原则:

//专门处理int类型的加法函数
int Add(int A, int B)
{
    return A+ B;
}

// 通用加法函数
template<class T> 
T Add(T& A, T& B)
{
 return A+ B;
}

int main()
{
   Add(1,2);//调用的是专门处理int类型的加法函数
   Add<int>(1,2);//调用的是通用加法函数
}
  1. 对于非模板函数和同名函数模板,如果其他条件都相同,在调动时会优先调用非模板函数而不会从该模
    板产生出一个实例。如果模板可以产生一个具有更好匹配的函数, 那么将选择模板
// 专门处理int的加法函数
int Add(int A, int B)
{
 return A+ B;
}

// 通用加法函数
template<class T1, class T2>
T1 Add(T1 A, T2 B)
{
 return A+ B;
}

void Test()
{
 Add(1, 2); // 与非函数模板类型完全匹配,不需要函数模板实例化
 Add(1, 2.0); // 模板函数可以生成更加匹配的版本,编译器根据实参生成更加匹配的Add函 数
}

二.类模板

1.类模板的定义格式

类模板的定义跟类函数的定义是相似的。
它的定义如下:

template<class T1, class T2, ..., class Tn>
class 类模板名
{
 // 类内成员定义
};

那么我们以下面的例子来讲解:

template<class T>
class Vector
{ 
public :

 // 使用析构函数演示:在类中声明,在类外定义。
 ~Vector();
 
 void PushBack(const T& data)void PopBack()// ...
 
 
 T& operator[](size_t pos)
 {
 assert(pos < _size);
 return _pData[pos];
 }
 
private:
 T* _pData;
 size_t _size;
 size_t _capacity;
};

// 注意:类模板中函数放在类外进行定义时,需要加模板参数列表
template <class T>
Vector<T>::~Vector()
{
 if(_pData)
 delete[] _pData;
 _size = _capacity = 0; 
}

2.类模板的实例化

类模板实例化需要在类模板名字后跟<>,然后将实例化的类型放在<>中即可,类模板名字不是真正的类,而实例化的结果才是真正的类。

如下:

//vector类名,vector<int>才是类型
vector<int>s1;
vector<char>s2;

模板不支持分离编译,也就是声明在.h ,定义在.cpp

以上是关于从零开始学c++————模板初阶的主要内容,如果未能解决你的问题,请参考以下文章

C++初阶第八篇——模板初阶(泛型编程+函数模板+类模板)

C++模板初阶

C++初阶---模板入门

<c++> 四模板初阶

C++模板初阶

C++模板初阶--懒人创造世界