C++模版初阶

在C++入门初阶时我们提到,为了让Swap函数既可以交换double类型数据,也可以交换int类型数据,C++实现了函数重载。但是我们重复的实现相同的逻辑依然显得较为臃肿,如何做才能只实现一次大致逻辑从而减少代码的拷贝呢?我们由此情景引入并介绍C++中的初阶模版。

1.泛型编程

定义:编写与类型无关的通用代码,是代码复用的一种手段

模版是泛型编程的基础。

2.模版初阶 

template<Typename T>

注意:T只能代表一种类型的参数,T的名字可以根据需要更换,但是我们一般习惯使用Type的 首字母缩写T。

typename需要几个定义几个,typename处也可以用class(class和typename可以互换)代替((切记:不能使用struct代替class)

例如之前的Swap:

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

                                     

报错的原因是,T只会被当作一种参数,T不能在作为int的同时作为double。

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

这样调整出两个参数就不会报错了,但是自然会产生精度的损失。

回到继续使用一个T的时候: 

我们通过调试观察两次调用:

           看起来去的是同一个函数(泛型的Swap),其实是简化给操作者看的。

但是其本质不是同一个函数,通过汇编我们会发现两次调用call的函数位置不一样。

两次call泛型的Swap的地址不一样。

模版原理:

编译器根据你的需要,用模版生成你需要的函数,也就是函数模版的实例化。

实质上会降低效率,因为编译器会根据你的数据类型进行推演,写出一个新函数。但是编译器自动生成也一定是比程序员手动实现更快的。

                                   

一个int 一个double时,推演实例化时会出现问题。

因为编译器不知道T该是什么类型。我们有以下三种处理方法:

第一种解决方案:强转         但是会丢失精度

                       

第二种解决方案:显式实例化(推荐使用)(显式实例化:在函数名后的<>中指定模板参数的实际类型

                     

                            第一次显示的指令T是int     第二次显式的指令T是double

第三种解决方案:改变返回值类型

                      

                      

简单返回T不可行。因为不知道返回值T的类型,无法返回

不过我们可以使用auto作为返回值类型。

template<typename T1,typename T2>
auto Add(const T1& x,const T2& y) {
	return x + y;
}

易错:

1.

                

此时编译器推演不出来T是什么类型,没有信息能让编译器推理。

所以此时必须使用显示实例化。

                               

2.

模版参数列表每一次使用之前都得再声明。

                        

或者我们声明类模版时,想在外部实现一个该函数,尽管我们在class Vector开始处就使用了参数列表T,再次实现该函数时还是得再写一次模版参数列表模版不建议声明和定义分离到.h 和.cpp会出现链接错误,具体原因后面会讲

template <class T>
Vector<T>::~Vector()
{
 if(_pData)
 delete[] _pData;
 _size = _capacity = 0;
}

3.

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


int a=1;double b= 1.1;
Swap<int>(a,b);

如上代码,会报错吗?

当然会报错。

显式实例化的int让T不再需要推理的过程,直接全部变成int,但是将b隐式类型转换过程中就涉及到生成临时变量,临时变量又具有常性,所以将b的int形式的拷贝赋值给T& y时,又造成了权限的放大,需要使用const修饰两个变量x y,才不会报错。

 

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


int a=1;double b= 1.1;
Swap<int>(a,b);

​

关于匹配的先后问题:

 普通函数和函数模版可以同时存在,编译器会优先去调用更匹配的。

有专门的int类(现成的普通函数)存在,则会优先走普通函数,不会去走模版(半成品)

但是如果只有一个现成的普通函数,也行(能隐式类型转换就行)

 4、有显示实例化时使用编译器生成的版本。

也就是说:

一个非模板函数可以和一个同名的函数模板同时存在,而且该函数模板还可以被实例化为这个非模板函
// 专门处理int的加法函数
int Add(int left, int right)
{
 return left + right;
}
// 通用加法函数
template<class T>
T Add(T left, T right)
{
 return left + right;
}
void Test()
{
 Add(1, 2); // 与非模板函数匹配,编译器不需要特化
 Add<int>(1, 2); // 调用编译器特化的Add版本
}

 5、优先使用"成品"

对于非模板函数和同名函数模板,如果其他条件都相同,在调动时会优先调用非模板函数而不会从该模 板产生出一个实例。如果模板可以产生一个具有更好匹配的函数, 那么将选择模板函数


3.类模板

(此处的class也可以用typename):

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

在C语言中,我们用typedef的方法决定向一个数据结构中放置哪种类型的数据:

                                

现在,我们可以通过类模版实现一个什么类型的数据都可以装的顺序表Vector。

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

但是模版不建议声明和定义分离到.h 和.cpp,会出现链接错误,具体原因后面会讲


将之前的DateType都换成T即可。

template<typename T>
class Stack
{
public:
	Stack(size_t capacity = 4)
	{
		_array = (T*)malloc(sizeof(T) * capacity);
		if (nullptr == _array)
		{
			perror("malloc申请空间失败");
			return;
		}
		_capacity = capacity;
		_size = 0;
	}

	void Push(const T& data);
private:
	T* _array;
	size_t _capacity;
	size_t _size;
};

易错:

1.类模板是一个类家族,模板类是通过类模板实例化的具体类。不要混淆模版类和类模版的概念。

2.模板类是一个家族,编译器的处理会分别进行两次编译,其处理过程跟普通类不一样。

同理,函数模版也是一个家族。

3.模板运行时不检查数据类型,也不保证类型安全,但是其具有较强的可移植性。

相关推荐

  1. C++模版

    2024-04-29 09:52:03       9 阅读
  2. C++】模板

    2024-04-29 09:52:03       38 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-04-29 09:52:03       18 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-04-29 09:52:03       19 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-04-29 09:52:03       19 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-04-29 09:52:03       20 阅读

热门阅读

  1. 【Go】关闭通道 用例展示

    2024-04-29 09:52:03       12 阅读
  2. 导航系统架构及业务模块组合策略

    2024-04-29 09:52:03       14 阅读
  3. 代谢组数据分析四:功能分析

    2024-04-29 09:52:03       9 阅读
  4. 探索数据之美:简述多元统计分析中的聚类分析

    2024-04-29 09:52:03       15 阅读
  5. 负载均衡简介

    2024-04-29 09:52:03       12 阅读
  6. tauri 2

    2024-04-29 09:52:03       12 阅读
  7. MySql: 可视化工具监测管理数据库

    2024-04-29 09:52:03       13 阅读
  8. Qt窗口

    Qt窗口

    2024-04-29 09:52:03      11 阅读
  9. 在k8s中以deployment方式部署minio

    2024-04-29 09:52:03       13 阅读
  10. 机器人项目相关

    2024-04-29 09:52:03       12 阅读