C++最强大的特性之一——模板Templates(上:模板函数)

 

本文为作者EricNTH的原创博客,允许转载但请务必注明出处!

嗨,大家好,我们又见面了,这两天我们来谈谈C++模板。

说到C++最强大,又最容易被忽略的特性,莫过于模板了。

//人尽皆知的STL除外
STL(Standard Template Library)就是由多个模板类与模板函数构成的。内容很多(vector,deque,queue,list,map,set,multimap,multiset,bitset,还有各种algorithm),当然也很复杂,小编相信大家也会不少,就不赘述了。
想想,可以说STL几乎99%都是用模板编写的,能不强大?

这两篇博客,我就带着大家一起来看看模板的两个重要组成部分:模板类和模板函数。

目录

1.C++最强大的特性之一——模板Templates(上:模板函数)—原创
2.C++最强大的特性之一——模板Templates(下:模板类)—原创

先给大家看一个实例:

#include<bits/stdc++.h>
using namespace std;

int main() {
	int a, b;
	a = 1;b = 2;
	swap(a,b);
	printf("a=%d b=%d\n", a, b);
	return 0;
}
#include<bits/stdc++.h>
using namespace std;

int main() {
	char a, b;
	a = 'a';b = 'b';
	swap(a,b);
	printf("a=%c b=%c\n", a, b);
	return 0;
}

两个程序都用了swap库函数,是被重载了吗?

那我们再来看看。

#include<bits/stdc++.h>
using namespace std;

class mynum {
	public:
		mynum(int ininum) {
			privnum=ininum;
		}
		~mynum(){}
		int Getnum(void) {
			return privnum;
		}
		void Setnum(int numtoset) {
			privnum=numtoset;
		}
	private:
		int privnum;
}

int main() {
	mynum a(1), b(2);
	swap(a,b);
	printf("a=%d b=%d\n", a.Getnum(), b.Getnum());
	return 0;
}

嗯,还是被交换了?这回我们自定义的类 C++库肯定不会给我们重载吧!
于是,我研究了一下源码。

//bits/move.h 164-178行
template<typename _Tp>
    inline void
    swap(_Tp& __a, _Tp& __b)
#if __cplusplus >= 201103L
    noexcept(__and_<is_nothrow_move_constructible<_Tp>,
	            is_nothrow_move_assignable<_Tp>>::value)
#endif
    {
      // concept requirements
      __glibcxx_function_requires(_SGIAssignableConcept<_Tp>)

      _Tp __tmp = _GLIBCXX_MOVE(__a);
      __a = _GLIBCXX_MOVE(__b);
      __b = _GLIBCXX_MOVE(__tmp);
    }

有很多看不懂的东西(那些_GLIBCXX啊,__cplusplus啊,noexcept啊),但是基本想法和我们还是一样的(tmp,a,b)

但是:我们可以肯定:只有这一个定义,没有重载。
嗯?这是怎么做到的?
答案只有两个字:模板
接下来,请让我和大家进行详细讲解。

模板函数的定义

template <type parameter list>
Type FunctionName (Parameter list) {
	//Functions...
}

好吧,我这样写估计没人可以看的懂,那我们来用自己看得懂的方式重写一遍swap函数吧。

template <typename _Tp>
void swap(_Tp & value1, _Tp & value2) {
	_Tp tmp;
	tmp = value1;
	value1 = value2;
	value2 = tmp;
}

注意:这里要用引用传递参数(&),否则不起作用。

模板的使用

如果我们用

swap<int> (a,b)

就相当于把模板实例化了。
就相当于现在有了这样一个函数:

void swap(int & value1, int & value2) {
	int tmp;
	tmp = value1;
	value1 = value2;
	value2 = tmp;
}

即:这是函数swap在_Tp=int是的一个实例

那如果我们这样使用呢?

swap(a,b);

其实,编译器很聪明,它会根据您传入的参数来自动辨别并实例化swap。所以上面的语句同样可以通过编译。

那有的朋友就要问了:那为什么不用宏呢?

宏与模板

#define max(a,b) ((a)>(b)?(a):(b))

其实等价于

template <typename _T>
_T max(_T a, _T b) {
	return (a > b) ? a : b;
}

注意:?:是C++中(唯一)的一个三目运算符。其作用是:a?b:c当a返回true时,a?b:c返回b,反之返回c,因此也被称作三目条件运算符

而且宏好像还更简短啊!

答案来了:宏不是类型安全的,破坏了C++的强类型特征。因此被某些C++程序员视为“瘟疫”。大家可以试试:

char a='a';
int b=1;
int maxn=max(a,b);

如果用的是宏,它可以通过编译:但是,这显然是不正确的!

其次,若max宏被写成这样:

#define max(a, b) a > b ? a : b

并且这样调用:

max(1+5, 2+7);

就会按照这样的方式展开:

1+5>2+7?1+5:2+7

明显,是有问题的。(这在我以后关于宏的博客上也会讲到。)

而且,宏完全以内联方式展开,这虽然避免了函数调用入栈出栈的开销,却会导致可执行文件膨胀。

而模板极好,极优雅地避免了这些不必要的麻烦(虽然会长一些)。

所以,建议大家尽可能用模板来代替宏

最后,给大家布置一些“家庭作业”。

练习题

1.仿照max函数,编写一个模板函数min,功能是求三个数中最小的一个。
2.自己上网搜索(或直接看STL的源码),了解模板类,下一篇博客我来给大家讲一讲。
3.大家可以把自己写好的min函数发至评论区或我的邮箱eric_ni2008@163.com,并可以告诉我您了解的模板类,下一篇博客我会展示所有写的好的,并“点名表扬”!

好了,今天就讲到这里了。下次我们再谈!喜欢请点赞~,同时,别忘了做家庭作业哈【笑】,下一篇博客我会公布答案!

本文链接https://www.cnblogs.com/EricNTH/p/12436254.html原创不易,转载请标明出处!

下一篇链接C++最强大的特性之一——模板Templates(下:模板类)—原创

大家再见!

 

posted @ 2020-02-27 13:48  EricNTH  阅读(328)  评论(0编辑  收藏  举报