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(下:模板类)—原创
大家再见!