C++ 函数 模板
1.模板
1.1 模板的概念
模板就是建立通用的模具,大大提高复用性
模板的特点:
1. 模板只是一个框架,不可以直接使用
2. 模板的通用并不是万能的
1.2 函数的模板
1.2.1 函数模板语法
函数模板语法:
建立一个通用函数,其返回值类型和形参可以不具体制定,用一个虚拟的类型代表
语法:
template<typename T>
解释:
template : 声明创建模板
typename: 表明后面的符号是一整数据类型,可以用class 代替
T: 通用的数据类型,名称可以替换,通常为大写字母
// main.cpp
#include <iostream>
#include <string>
using namespace std;
// 函数模板
// 两个整形交换函数
// void swap(int &a, int &b){
// int temp = a;
// a = b;
// b = temp;
// }
// // 两个浮点型交换函数
// void swap(float &a, float &b){
// float temp = a;
// a = b;
// b = temp;
// }
// 函数模板
template <typename T> // 声明一个模板,告诉编译器后面代码找那个紧跟着的T不要报错,T是一个通用数据类型
void swap1(T &a, T &b){
T temp = a;
a = b;
b = temp;
}
void test01() {
int a = 10;
int b = 20;
// 两种函数调用
// 1. 自动类型推导
// swap1(a, b);
// 2. 指定类型
swap1<int>(a, b);
cout << "a = " << a << endl;
cout << "b = " << b << endl;
}
int main() {
test01();
return 0;
}
1.2.2 函数模板注意事项
函数模板注意事项
自动类型推导,必须推导出一致的数据类型T,才可以使用
模板必须要确定出T的类型,才可以使用
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
// 函数模板注意事项
// 1.自动类型推导,必须推导出一致的数据类型T,才可以使用
template<class T> // typename 可以用class代替
void mySwap(T& a, T& b){
T temp = a;
a = b;
b = temp;
}
// 2.模板必须要确定出T的类型,才可以使用
template<class T>
void func(){
cout << "func 调用" << endl;
}
void test02() {
func<int>(); // 必须要确定出T的类型,才可以使用
// fun();// 不加typename编译器无法推导出T的准确数据类型
}
void test01() {
int a = 10;
int b = 20;
char c = 'c';
// 利用自动类型推导,不加typename编译器无法推导出T的准确数据类型
mySwap(a, b);
// mySwap(a, c); // 编译器无法推导出T的准确数据类型
cout << "a = " << a << endl;
cout << "b = " << b << endl;
}
int main(int argc, char const *argv[]) {
test02();
return 0;
}
总结:使用模板时必须确定出通用数据类型T,并且能够推导出一致的类型
1.2.3 函数模板案例
实现通用 对数组进行排序的函数
规则 从大到小
算法 选择排序
测试 char数组 int数组 自定义数据类型数组
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
// 交换函数模板
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; i++)
{
// 记录下标
int index = 0;
for (int j = 1; j < len - i; j++)
{
// 从大到小 或者从小到大,只需要更改这里
if (arr[j] < arr[index])
{
index = j;
}
}
mySwap(arr[len - i - 1], arr[index]);
}
}
// 打印数据模板
template<class T>
void printArray(T arr[], int len){
for (size_t i = 0; i < len; i++)
{
cout << arr[i] << " ";
}
}
void test01(){
// char数组
char charArr[] = "headbfgsvw";
int len = sizeof(charArr) / sizeof(char);
mySort(charArr, len);
printArray(charArr, len);
cout << endl;
// int数组
int intArr[] = {1, 3, 5, 7, 9, 2, 4, 6, 8, 0};
len = sizeof(intArr) / sizeof(int);
mySort(intArr, len);
printArray(intArr, len);
}
int main(int argc, char const *argv[]) {
test01();
return 0;
}
1. 2.4 普通函数和函数模板的区别
普通函数和函数模板的区别:
1. 普通函数调用时可以发生隐式类型转换(隐式类型推导)
2. 函数模板调用时,如果利用实参进行类型推导,不会发生隐式类型转换
3. 如果利用显示指定类型的方式,可以发生隐式类型转换
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
// 普通函数
int myAdd(int a, int b){
return a + b;
}
// 函数模板
template<class T>
T myAddO2(T a, T b){
return a + b;
}
void test01(){
int a = 10;
int b = 20;
char c = 'c';
cout << "普通函数调用结果:" << myAdd(a, c) << endl;
// 自动类型推导,不能发生隐式类型转换
// cout << "函数模板调用结果:" << myAddO2(a, c) << endl;
// 显示指定类型,可以发生隐式类型转换
cout << "函数模板调用结果:" << myAddO2<int>(a, c) << endl;
}
int main(int argc, char const *argv[]) {
test01();
return 0;
}
2.5 模板函数与普通函数的调用规则
模板函数与普通函数的调用规则
1. 如果普通函数和模板函数都可以调用,优先调用普通函数
2. 可以通过空模板参数列表来强制调用模版函数
3. 函数模板可以发生函数重载
4. 如果函数模板可以产生更好的匹配,优先调用函数模板
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
// 普通函数
void myPrint(int a, int b){
cout << "普通函数调用" << "a = " << a << ", b = " << b << endl;
}
// 函数模板
template <typename T>
void myPrint(T a, T b){
cout << "函数模板调用" << "a = " << a << ", b = " << b << endl;
}
// 函数模板
template <typename T>
void myPrint(T a, T b, T c){
cout << "重载函数模板调用" << "a = " << a << ", b = " << b << ", c = " << c << endl;
}
void test01(){
int a = 10;
int b = 20;
int c = 'c';
myPrint(a, b); // 普通函数调用
myPrint<>(a, b); // 通过空模板参数列表,强制调用模板函数
myPrint(a, b, c); // 重载函数模板调用
// 如果函数模板可以产生更好的匹配,优先调用函数模板
char ch = 'a';
char ch2 = 'b';
myPrint(ch, ch2); // 函数模板调用
}
int main(int argc, char const *argv[]) {
test01();
return 0;
}
// 总结
// 既然提供了函数模板,最好旧不要提供普通函数,否则容易出现二义性
1.2.6 模板的局限性
局限性:
模板的通用性并不是万能的
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
// 模板的局限性
// 模板不是万能的,有些特定数据类型,需要用具体化方式做特殊处理。
//对比两个数据是否相等
template<class T>
bool compare(const T& v1, const T& v2){
if (v1 == v2)
{
return true;
}
else
{
return false;
}
}
class Person{
public:
Person(string name, int age):name(name),age(age){};
string name;
int age;
};
// 针对Person类特化版本,具体化优先调用
template<> bool compare(const Person& p1, const Person& p2){
if (p1.name == p2.name && p1.age == p2.age){
return true;
}
return false;
}
void test01(){
int a = 10;
int b = 20;
bool ret = compare(a, b);
if (ret)
{
cout << "a == b " << endl;
}
else{
cout << "a != b" << endl;
}
}
void test02(){
Person p1("Tom", 10);
Person p2("Tom", 10);
bool ret = compare(p1, p2);
if (ret)
{
cout << "p1 == p2" << endl;
}
else{
cout << "p1 != p2" << endl;
}
}
int main(int argc, char const *argv[]) {
test02();
return 0;
}
总结
1. 利用具体化模板,可以解决自定类型的通用化
2. 学习模板并不是为了写模板,而是在STL能够运用系统提供的模板