(十三)C++进阶之函数模板(一)

1.1、何为函数模板

在引出这个概念之前,我们先来看一段例子:

#include <iostream>

using namespace std;

void my_swap(int &a, int &b)
{
	int temp = a;
	a = b;
	b = temp;
}

void my_swap(double &a, double &b)
{
	double temp = a;
	a = b;
	b = temp;
}

int main(void)
{
	int a = 10;
	int b = 20;

	double da = 1.34;
	double db = 6.9;

	my_swap(a,b);
	cout << "a = " << a << " b = " << b << endl;
	my_swap(da, db);

	cout << "da = " << da << " db = " << db << endl;

	return 0;
}

这个例子很简单,主要是对函数my_swap的重载来实现不同类型的交换。
运行结果:
在这里插入图片描述

经过这个例子我不禁在想,如果我出现一百个这种类型的函数重载,但是到最后我发现这种实现方法某个地方出现问题,那么我岂不是要将这一百个的函数全部修改?

那工作量就不是那么简单了,那有没有什么办法可以解决这个问题?

我打个比方,我们制作手机外壳,先是有手机外壳模子,然后根据模子的形状来生产最终的手机壳,哪怕我最终发现手机壳有问题,我只需要修改手机壳的模子,后面生产就没必要对每个手机壳进行修改。

同理函数是否会有一个模板,后面的函数实现是根据这个模板来实现。

这就是我今天想要引出来的函数模板概念。

格式如下:
在这里插入图片描述

所以我将上面的那个例子修改一下。

#include <iostream>

using namespace std;


template<class T>
void my_swap(T &a, T &b)
{
	T temp = a;
	a = b;
	b = temp;
}

int main(void)
{
	int a = 10;
	int b = 20;

	double da = 1.34;
	double db = 6.9;

	my_swap(a, b);
	cout << "a = " << a << " b = " << b << endl;
	my_swap(da, db);

	cout << "da = " << da << " db = " << db << endl;

	return 0;
}

其中最终要得是这个模板函数:

template<class T>
void my_swap(T &a, T &b)
{
	T temp = a;
	a = b;
	b = temp;
}

以下几点注意点:
1、格式使用:

template<class T>

也可以改成这样:

template<typename T>

这两种那种都可以,根据自己喜欢来即可。

2、模板作用区域
当我们使用template定义后,紧跟随的第一个函数才是模板函数,后面将不在是。
在这里插入图片描述

除非你在前面再加上模板的定义。
在这里插入图片描述

所以总结一下模板的概念:

1、template是语义是模板的意思,尖括号中先写关键字typename或是class,后面跟一个类型T,此类即是虚拟的类型。至于为什么用T,用的人多了,也就是T了。

2、调用过程是这样的,先将函数模板实再化为函数,然后再发生函数调用,所以会出现两次编译。

1.2、函数模板的显示和自动类型转换

前面的那个例子中,我们并未指明T的类型,但是系统却能正确调用,所以这里存在一个自动转换的过程,也就是编译器会根据你传进来的数据类型进行处理。
在这里插入图片描述

当我们需要指明的时候怎么定义呢?

	my_swap<int>(a, b);
	cout << "a = " << a << " b = " << b << endl;
	my_swap<double>(da, db);

只需要加个<>里面填充你想要将T转换的类型。

1.3、函数模板和函数重载

在函数模板和函数重载定义在一起的时候,我们需要特别注意:

普通函数会进行隐士的数据类型转换,  函数模板不提供隐式的数据类型转换必须是严格的匹配,所以你想要执行函数模板生成的函数的话,那么必须严格的匹配,否则他会优先调用普通函数。

我们看一下这部分代码:
在这里插入图片描述

普通函数是可以进行隐式转换的,但是函数模板直接报错了,因为他报错无法匹配和模板函数相同的参数列表,所以直接报错。

我们调试一下,发现在重载函数和模板函数参数列表都满足的情况下,他会优先调用普通函数,而不是函数模板。
在这里插入图片描述

这时候我们修改重载函数的里面的参数列表由之前的int类型变为char,这时候,唯一能够满足参数列表的只有函数模板了,但是发现他还是调用普通函数,也就是说,不管是隐式的转换(重载),还是显示的,只要能满足调取普通函数的,就优先调取普通函数,而不是函数模板。

这时候我将重载函数的参数列表修改为隐式也无法转换,这时候才会发现他调用函数模板了,所以在使用函数模板和函数重载时候需要注意一下他们调用的顺序。
在这里插入图片描述

1.4、函数模板的编译原理

1、浅析编译过程原理
在这里插入图片描述
这是在linux下的编译过程:

g++  -E  hello.c  -o  hello.i(预处理)
g++  -S  hello.i  -o  hello.s(编译)
g++  -c  hello.s  -o  hello.o(汇编)
g++  hello.o  -o  hello(链接)

当然,以上四个步骤,可合成一个步骤:
g++  hello.c  -o  hello(直接编译链接成可执行目标文件)

打开我们电脑的虚拟机里面的Ubuntu(没有的同学可以自己安装哈),写下面的代码:
在这里插入图片描述

然后我们将他编译成汇编文件,直接使用这个命令:
g++ -S test.cpp -o test.s
然后查看一下里面关于函数模板的使用。
我们找到main函数的地方,然后看到调取函数的两个地方,我不是很懂汇编,但是看到call就是调取的意思,然后我们看一下这两个函数,他们唯一区别的就是一个是i,一个是d,其实也好理解,一个是整形,一个是double型。
在这里插入图片描述

然后我们在增加一对int的来看一下他是否会创建第三个函数:
在这里插入图片描述

我们发现他们是一样的,所以可以得到一个结论,只有形参列表不同才会创建新的函数专门处理,而不是传递一个参数就是用一个函数。
在这里插入图片描述

最后我们来看一张对比图:
在这里插入图片描述

这时候我们回到刚开是的那个问题,如果我有一百个函数使用该函数模板,如果那天我发现出现问题了,只需要修改函数模板即可,而不是去修改一百个函数,这只是函数模板的一个好处,还有别的好处,遇到在总结了。

发布了29 篇原创文章 · 获赞 0 · 访问量 409

猜你喜欢

转载自blog.csdn.net/weixin_42547950/article/details/104455573