C++模板

目录

1.泛型编程

需求

函数重载

2.函数模板

概念

模板格式 

原理

实例化(调用)

1.隐式实例化

2.显式实例化

模板参数匹配原则 

3.类模板 

类模板的定义格式

类模板的实例化

1.泛型编程

泛型编程:编写与类型无关的通用代码,是代码复用的一种手段。模板是泛型编程的基础。

需求

世界是懒人创造的,有需求才会有进步,生活中,我们可以用一个模子就刻出好多个一模一样的玩意

那么,有没有办法在写代码的时候,我们自己刻一个模子,然后写一次,用一辈子呢?答案是肯定的,要不然我写这干嘛,对吧

例子

比如说交换函数,之前我们写交换函数,要满足各种类型,只能函数重载,就要写好多份,int的来一份,char的来一份,double的来一份等等,但是我们可以想办法写一份模子代码,我填充进去int,他就给我计算int类型的,填充进去double,他就要给我计算double类型的

函数重载

void Swap(int& left, int& right)
{
int temp = left;
left = right;
right = temp;
}
void Swap(double& left, double& right)
{
double temp = left;
left = right;
right = temp;
}
void Swap(char& left, char& right)
{
char temp = left;
left = right;
right = temp;
}
......

 接下来是懒人偷懒的函数模板

2.函数模板

概念

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

其实就是写一份通用代码,比如说相加,既可以加int,也可以加double,还可以加char,而且保证精准.

模板格式 

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

看不懂没关系,底下有例子

template<typename T>
void Swap( T& left, T& right)
{
T temp = left;
left = right;
right = temp;
}

 typename是用来定义模板参数关键字,也可以使用class(不能使用struct代替class)

这个template是什么,老实说我也不清楚,但是无所谓,我们知道定义函数模板的时候是template后面一个<>,里面放着几个类型为typename的参数就行,比如说我函数里面有三种类型的参数或者数据,那我就定义三个(typename T1, typename T2,typename T3)就行.底下写代码,把数据类型改为你定义的那种就行了

注意:必须是<typename T1, typename T2,typename T3>这种,也就是typename或者class必须在每个T之前都加,形如这种<typename T1, T2, T3>是错误的,至于原因,没有原因,规矩.

原理

函数模板是一个蓝图,它本身并不是函数,是编译器用使用方式产生特定具体类型函数的模具。所以其实模板就是将本来应该我们做的重复的事情交给了编译器

在编译器编译阶段,对于模板函数的使用,编译器需要根据传入的实参类型来推演生成对应类型的函数以供调用。比如:当用double类型使用函数模板时,编译器通过对实参类型的推演,将T确定为double类型,然后产生一份专门处理double类型的代码,对于字符类型也是如此。

概括起来就是,编译器在编译的时候,比如你调用了上面那个交换函数的模板代码,传递的两个参数都是int,那编译器就会推理得到T等于int,然后再生成一份代码,里面的T都变成了int(如果你问为什么不直接把T换成int,那如果马上有调用模板,参数传double类型,编译器不就没辙了么) 

实例化(调用)

1.隐式实例化

隐式实例化:让编译器根据实参推演模板参数的实际类型 

没什么好解释的,就字面意思

//交换函数模板
template<typename T>
void Swap(T& a, T& b) {
	T temp = a;
	a = b;
	b = temp;
}

int main() {
	double a = 10.5;
	double b = 20.5;
	Swap(a, b);
	cout << a << endl;
	cout << b << endl;
	return 0;
}

 

我们可以看到,调用上面的交换函数,已经完成了交换,但是我们并没有写double类型的代码

但是要注意,类似于这种在隐式实例化的时候是有问题的

double a = 10.5;
	int b = 20;
	Swap(a, b);

 这个就不能通过编译,你传了个a,编译器一推导,T应该是int吧,结果后面又出现了double类型,编译器就看不懂了,它也不知道该怎么转化,就只能报错了

 这种情况,一般有两种解决办法(第二种不是所有情况下都能解决这些问题,推荐用第一种,比如这道题,就不适合)

第一种:你传参的时候,来一个强制转化,把其中一个转化成另一个

第二种:使用显式实例化

2.显式实例化

 显式实例化:在函数名后的<>中指定模板参数的实际类型

就是也别让编译器推导了,调用的时候直接告诉编译器就行了

//交换函数模板
template<class T>
void Swap(T& a, T& b)
{
	T temp;
	temp = a;
	a = b;
	b = temp;
}

int main() {
	int a = 10;
	int b = 20;
	Swap<int>(a, b);  //显式化
	cout << a << endl;
	cout << b << endl;
	return 0;
}

如果类型不匹配,编译器会尝试进行隐式类型转换,如果无法转换成功编译器将会报错。

模板参数匹配原则 

一个非模板函数可以和一个同名的函数模板同时存在,而且该函数模板还可以被实例化为这个非模板函数(就是说你写了模板函数,也可以再写专门某个类型的函数,当你调用那个类型时,会优先使用专门那个类型的函数)

对于非模板函数和同名函数模板,如果其他条件都相同,在调动时会优先调用非模板函数而不会从该模板产生出一个实例。如果模板可以产生一个具有更好匹配的函数, 那么将选择模板(这句话前半句已经解释过了,后半句话的意思看代码)

// 专门处理int的加法函数
int Add(int left, int right)
{
return left + right;
}
// 通用加法函数
template<class T1, class T2>
T1 Add(T1 left, T2 right)
{
return left + right;
}
void Test()
{
Add(1, 2); // 与非函数模板类型完全匹配,不需要函数模板实例化
Add(1, 2.0); // 模板函数可以生成更加匹配的版本,编译器根据实参生成更加匹配的Add函数
}

模板函数不允许自动类型转换,但普通函数可以进行自动类型转换.(就是说最好不要参数只有一个T类型,你却给两个参数传递了不同的类型,就像在隐式实例化那里写的代码就最好不要写)

3.

目录

1.泛型编程

需求

例子

函数重载

2.函数模板

概念

模板格式 

原理

实例化(调用)

1.隐式实例化

2.显式实例化

模板参数匹配原则 

3.类模板 

类模板的定义格式

类模板的实例化


类模板 

类模板的定义格式

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

注意:类模板中函数放在类外进行定义时,需要加模板参数列表

比如说有一个类模板名字是vector,在类外定义这个类的析构函数,就需要这样定义

template <class T>
Vector<T>::~Vector()
{
...
}

类模板的实例化

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

说白了就是编译器不猜了,你得直接告诉它,这个T到底是什么

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值