在昨天学习了函数模板的基本概念、语法以及注意事项后,那么现在不妨用个小案例来检验下自己的学习成功吧!
函数模板案例
案例描述
- 利用函数模板封装一个排序函数,可以对不同数据类型数组进行排序
- 排序规则从小到大,排序算法为选择排序
- 分别利用char数组和int数组进行测试
当我看到案例要求用选择排序算法来完成时,我的大脑对其竟然是几近空白。虽然我之前学过冒泡、选择、插入等排序算法,但由于太久没有用到,我几乎快忘干净了。我仔细回想之前所学的知识,但脑海里只有冒泡排序有个大致思路,插入排序有些许印象,而选择排序我几乎忘光了…我内心很纠结,在想我到底是该就案例把选择排序草草过一遍呢?还是老老实实地去找个讲选择排序的视频再学一边呢?最后理智还是战胜了懒惰,我又去找了个讲选择排序的视频把它老老实实地学了一遍。接下来我把我对选择排序的理解进行一个大致的讲解
选择排序
- 本质:遍历数据并进行排序
- 特点:
1、对于n个数据,只需要执行n-1轮
2、需要定义一个额外变量来记录未排序区域中最小元素的位置
3、 每一轮只执行一次交换操作
以下为选择排序的C语言代码
for (int i = 0;i < n - 1;i++) //外层循环 n-1表示选择排序需要进行的次数
{
int p = i; //初始化 先默认最小元素下标为0 p是用来记录最小元素位置的元素
for (int j = 1 + i;j < n;j++) //内层循环
{
if (arr[j] < arr[p]) //比较大小
{
p = j; //赋值语句
}
}
int temp = arr[i]; //交换元素语句
arr[i] = arr[p];
arr[p] = temp;
}
好的,在我又学习了一遍选择排序后,我感到无比开心,庆幸自己没有放过自己,接下来进入正题
案例实现
思考:既然是函数模板的案例,而案例描述中又提到要分别用char数组和int数组进行测试,那么为了提高代码复用性,则必须要在此案例中的交换函数和选择排序算法中使用到函数模板相关的知识,具体代码如下:
- 交换函数模板
template<class T> //声明创建模板
void mySwap(T& a, T& b) //交换函数模板
{
T temp = a;
a = b;
b = temp;
}
- 选择排序算法模板
template<class T> //声明创建模板
void mySort(T arr[],int len) //选择排序算法模板
{
for (int i = 0;i < len - 1;i++)
{
int max = i;
for (int j = 1 + i;j < len;j++)
{
if (arr[max] < arr[j])
{
max = j;
}
}
if (max != i)
{
mySwap(arr[max], arr[i]);
}
}
}
既然模板有了,那么就该上案例了
void test01()
{
//测试char数组
char charArr[] = "bacdfe";
int num = sizeof(charArr) / sizeof(char);
mySort(charArr, num);
printArray(charArr,num);
}
void test02()
{
//测试int数组
int intArr[] = {5,2,6,3,1,4};
int num = sizeof(intArr) / sizeof(int);
mySort(intArr, num);
printArray(intArr, num);
}
MVP结算画面:
在成功完成函数模板案例后,我不禁想问:普通函数和函数模板有什么区别呢?诶!接下来的学习恰好解决了我的疑惑
普通函数和函数模板的区别
普通函数和函数模板的区别:
- 普通函数调用时可以自动发生类型转换(隐式类型转换)
int myAdd01(int a, int b)
{
return a + b;
}
void test01()
{
int a = 10;
int b = 20;
char c = 'c';
cout << myAdd01(a, c) << endl;
}
普通函数调用时可以发生隐式类型转化,将char型的字符“c”转化成int型的ASCII码表值——99,故最终相加结果为10+99=109
- 函数模板调用时,若用显示指定类型,可以发生隐式转换
在“<>”中明确指定T的类型,告诉编译器不用去推导了,非int类型直接转成int类型即可
- 函数模板调用时,若用自动类型推导,则不可以发生隐式转换
一个int类型,一个char类型,编译器无法推导出一致的T的类型,故不可以发生隐式类型转换
总结:建议使用显示指定类型的方式调用函数模板,因为这样可以自己确定通用类型T
了解完普通函数和函数模板的区别后,那么,我什么时候应该调用普通函数,什么时候应该调用函数模板呢?是不是应该去学一下?没错,我又学了一下下
普通函数和函数模板调用规则
-
如果函数模板和普通模板都可以调用,优先调用普通函数
注意:若此时普通函数只有声明没有实现,也不会调用函数模板,而是会报错 -
可以通过空模板参数列表强制调用函数模板
注意:哪怕普通函数和函数模板同时存在,因为有了空模板参数列表“<>”,也会强制调用函数模板 -
函数模板可以发生函数重载
-
如果函数模板可以产生更好的匹配,优先调用函数模板
注意:此时调用函数模板而不调用普通函数的原因是——调用普通函数还要发生隐式类型转换,编译器嫌麻烦,不如调用函数模板做自动类型推导
学完了这些之后,我感觉函数模板好强大呀,只要正确掌握其用法,便可以极大地提高编程效率,让我无往不利。但现实,真的如我想象的那般美好吗?
模板的局限性
哈哈哈哈哈,函数模板果然不是万能的!我就知道它有它的局限性。正如我沏茶时总偏爱紫砂壶的温润,可煮咖啡时却不得不换用玻璃壶的透亮。人类文明史上,青铜器替代了石器却也始终无法取代玉器的礼器地位,正如我们既需要模板的抽象之美,又必须接纳其局限的现实。我也终于明白:承认边界的智慧,恰是突破边界的开始…
- 模板并非万能,有些特定数据类型,需要用具体化方式做特殊实现
总结: - 利用具体化模板,可以解决自定义类型的通用化
- 学习模板不是为了写模板,而是在STL中能够运用系统提供的模板
哈基模,你这家伙,我学了半天怎么写具体化的模板来解决自定义类型的通用化,你却最后告诉我其实只要会在STL中会使用系统提供的模板就可以了!?哈基模,那我刚刚吃过的那些屎算什么…我强忍着恶心好不容易理清了思路,你却告诉我这些都是我的一厢情愿…唉,终究是我错付了…
正当我失意之际,我又偶然得知你居然还有私生子!?!继承了你一部分特性的类模板?!!这无疑又给了我当头一棒…哈基模,我已经找不到任何言语来形容你的所作所为了。凌晨三点,我仍未眠…