C++练级计划 -> 《模版》泛型编程

目录

泛型编程

函数模版

模版格式:

函数模版实例化:

隐式实例化

显示实例化

函数模版的匹配

1.如果有函数匹配,则优先调用函数而不是推导模版

2.如果没有函数匹配,则优先推导模版

3.隐式实例化的模版不可以参数类型不匹配

类模板

两个注意点:

1.如果需要函数声明与定义分离,则需要在定义时,也要有模版头

2.类模版的实例化虽然也有隐式实例化,但是不建议使用,应该明确指出需要的类型。


泛型编程

首先根据字面意思理解一下:广泛类型的编程。

C++中有一个特性就是可以进行函数重载。比如:

// 交换两个整型
void Swap(int& x, int& y)
{
	int tmp = x;
	x = y;
	y = tmp;
}
// 交换两个双精度浮点型
void Swap(double& x, double& y)
{
	double tmp = x;
	x = y;
	y = tmp;
}

上述代码就是一个swap函数的重载。

重载:C++中同名函数可以出现多个,只要参数不是完全一样即可。因为和c语言直接以函数名保存函数不同,C++在保存函数时,是以函数名和参数一起保存的。

在重载时,是直接生成了对应的函数

但是和泛型编程有什么关系呢? 

和重载不同的在于,泛型编程是将函数变成了一个模版,再通过传入的参数确定需要什么类型的函数,再进行生成。传入多组不同类型的数据时,这个模版也就变成了一个重载函数了。是一种代码复用的手段

可以把泛型编程当做抖音一个爆火的舞蹈,都是一种舞蹈,但是有不同的人会来跳,来拿流量。

函数模版

上面我们说泛型编程就是一个套模版。所以下面的代码,就是给了一个函数模版 Swap。其中T根据传入参数的类型,自动变成这个类型。

template<typename T>
void Swap(T& x, T& y)
{
	T tmp = x;
	x = y;
	y = tmp;
}

 所以说函数模版是什么很明了了,技术的发展都是朝着越来越简便简单发展的。模版的出现大大简化了程序员敲代码时重复函数的书写。有句话说的好:懒人创造世界。

模版格式:

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

从格式中可以看出,T不只是可以一个,也可以多个。 typename 可以用 class替换,但是不可以用struct。

函数模版实例化:

实例化:本来这只是一串代码,实例化就是对这串代码进行使用,这时就会开辟空间给这块代码的执行。

这里函数模版的实例化就是用不同的参数类型使用模版,类似于函数重载,每次使用不同的参数进入模版时,会生成一个新的函数重载进行使用

两种类型:隐式实例化和显示实例化

隐式实例化

隐式就是隐藏,我们不指定出我们要使用的类型,而是让编译器自己去推演

#include <iostream>
using namespace std;
template<typename T>
T Add(const T& x, const T& y)
{
	return x + y;
}
int main()
{
	int a = 10, b = 20;
	int c = Add(a, b); //编译器根据实参a和b推演出模板参数为int类型
	return 0;
}

如上述代码:Add函数就是传入两个int变量,然后编译器根据模版会推演出int版本的add。 

但是对于这样的实例化不可以传两个类型不一样的变量。

	int a = 10;
	double b = 1.1;
	int c = Add(a, b);

如上代码我们传入的是:double和int ,模版要求的是严格匹配,所以在遇到参数a时开出来的Add就是int类型,double和int 虽然平时可以转换,但是模版要求严格匹配所以此时就会报错。

解决方法有两个:1.在传入前就把double变量强转为int

    int a = 10;
    double b = 1.1;
    int c = Add(a, (int)b);

 2.就是下面的显示实例化

显示实例化

显示实例化就是在函数调用时用<>将模版类型提前输入,这样就生成了int 的函数,然后b就会在进入函数前就隐式转换为int。

#include <iostream>
using namespace std;
template<typename T>
T Add(const T& x, const T& y)
{
	return x + y;
}
int main()
{
	int a = 10;
	double b = 1.1;
	int c = Add<int>(a, b); //指定模板参数的实际类型为int
	return 0;
}

为什么显示实例化可以隐式类型转换,而上面的隐式实例化不能呢?

可以这样想:显示实例化是在模版未推导前生成了一份int 的 add函数,这个函数的使用方法和以往的函数一样,所以可以隐式类型转换,就是在使用函数前将参数进行转换后再传入函数。但是我们的隐式实例化是先推导出类型再形成函数,模版的推导是必须严格匹配的,所以这时不会进行隐式类型转换。

函数模版的匹配

1.如果有函数匹配,则优先调用函数而不是推导模版

#include <iostream>
using namespace std;
//专门用于int类型加法的非模板函数
int Add(const int& x, const int& y)
{
	return x + y;
}
//通用类型加法的函数模板
template<typename T>
T Add(const T& x, const T& y)
{
	return x + y;
}
int main()
{
	int a = 10, b = 20;
	int c = Add(a, b); //调用非模板函数,编译器不需要实例化
	int d = Add<int>(a, b); //调用编译器实例化的Add函数
	return 0;
}

如上代码:如果我们两个参数都是int,则就优先调用已经有的int版的Add。但是如果有显示实例化了,那优先使用模版实例化的函数(因为显示实例化是模版的,普通函数没有这种规则。) 

2.如果没有函数匹配,则优先推导模版

#include <iostream>
using namespace std;
//专门用于int类型加法的非模板函数
int Add(const int& x, const int& y)
{
	return x + y;
}
//通用类型加法的函数模板
template<typename T1, typename T2>
T1 Add(const T1& x, const T2& y)
{
	return x + y;
}
int main()
{
	int a = Add(10, 20); //与非模板函数完全匹配,不需要函数模板实例化
	int b = Add(2.2, 2.2); //函数模板可以生成更加匹配的版本,编译器会根据实参生成更加匹配的Add函数
	return 0;
}

如上代码:a 调用的依旧是已有的int版Add,但是b由于找不到匹配的函数,所以它先推导模版实例化,再使用实例化的函数。 

3.隐式实例化的模版不可以参数类型不匹配

#include <iostream>
using namespace std;
template<typename T>
T Add(const T& x, const T& y)
{
	return x + y;
}
int main()
{
	int a = Add(2, 2.2); //模板函数不允许自动类型转换,不能通过编译
	return 0;
}

和上述显示实例化和隐式实例化解释相同; 

类模板

和函数模版的定义类似都是在类名或者函数名上面加template<typename T ....>

template<class T>
class Score
{
public:
    void Print()
    {
        cout << "数学:" << _Math << endl;
        cout << "语文:" << _Chinese << endl;
        cout << "英语:" << _English << endl;
    }
private:
    T _Math;
    T _Chinese;
    T _English;
};

两个注意点:

1.如果需要函数声明与定义分离,则需要在定义时,也要有模版头

template<class T>
class Score
{
public:
	void Print();
private:
	T _Math;
	T _Chinese;
	T _English;
};
//类模板中的成员函数在类外定义,需要加模板参数列表
template<class T>
//上面就是模版头,只有声明与定义分离时使用,如果在类内定义就不需要了
void Score<T>::Print()
{
	cout << "数学:" << _Math << endl;
	cout << "语文:" << _Chinese << endl;
	cout << "英语:" << _English << endl;
}

2.类模版的实例化虽然也有隐式实例化,但是不建议使用,应该明确指出需要的类型。

    //对的使用
	Score<int> s1;
	Score<double> s2;
    Score s1(3);//这是根据3使用构造函数,推导出Score<int> s1(3),来使用的
    
    //错的使用
    Score s1;//这样既没有参数给构造函数,也没有指定类模版的类型,这样是错的

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值