之前我们知道的交换两个变量的方法有宏定义、函数,这两种方式都能实现两个变量的交换,但是各有各的优缺点
宏定义:
- 优点:代码复用,适合所有的类型
- 缺点:缺少类型检查,宏在预处理阶段就被替换掉,编译器并不知道宏的存在
函数:
- 优点:真正的函数调用,编译器对类型进行检查
- 缺点:类型不同需要重复定义函数,代码无法复用
上边两种方式都各有利弊,但是在C++中,存在泛型编程的概念:即不考虑具体数据类型的编程方式(如下)
C++中的泛型编程有函数模板与类模板,这章我们先来了解函数模板
函数模板是一种特殊的函数,可以使用不同的类型进行调用,对于功能相同的函数,不需要重复编写代码,并且函数模板与普通函数看起来很类似,区别就是类型可以被参数化
函数模板通过template与typename两个关键字来定义,class也可以声明泛指类型,如下
以上内容来自:https://blog.csdn.net/lms1008611/article/details/81985815
下面是一些函数模板的实现:
#include<iostream>
using namespace std;
template <typename T>//声明即将定义一个模板了
void my_swap(T& a, T& b)
{
T temp = a;
a = b;
b = temp;
}
int main()
{
double i=1.025, j=1.075;
int m = 1, n = 2;
//编译器会推断T的数据类型
my_swap(i, j);
//显式指定类型,指定T的类型
my_swap<double>(i, j);
cout << "i=" << i << endl;
cout << "j=" << j << endl;
//第二次套用模板
my_swap(m, n);
//显式指定类型,指定T的类型
my_swap<int>(m, n);
cout << "m=" << m << endl;
cout << "n=" << n << endl;
return 0;
system("pause");
}
注意事项:1.你要能让编译器判断出来你T的类型,不能出现二义性,例如:
my_swap(i, n);
2.不能出现模糊定义的概念,模板必须要确定出T的数据类型,即使你在整个模板函数中没有用到T,你又不显式的指定T的数据类型,那就废了,编译器也不会自动搞明白你的T的类型,程序就会报错。
Tips:使用模板时必须确定出通用数据类型T,并且能够推导出一致的类型。
给出一个算例:
案例描述:
* 利用函数模板封装一个排序的函数,可以对**不同数据类型数组**进行排序
* 排序规则从小到大,排序算法为**选择排序**
* 分别利用**char数组**和**int数组**进行测试
#include<iostream>
using namespace std;
template <typename T>//声明即将定义一个模板了
void my_swap(T& a, T& b)
{
T temp = a;
a = b;
b = temp;
}
template<typename T>
void my_sort(T arry[],const int& length) {//形参数组名,共同拥有一段数组内存空间
//选择排序法
for (int i = 0; i < length; i++)
{
int min=i;
for (int j = i + 1; j < length; j++)
{
if (arry[min] > arry[j])
min = j;
}
if (min != i)
my_swap(arry[min], arry[i]);
}
}
template<typename T>
void my_print(T arry[],const int& length)
{
for (int i = 0; i < length; i++)
{
cout << arry[i] << "\t";
}
cout << endl;
}
int main()
{
char Arry[] = "asdfghjkl";
int length = sizeof(Arry) / sizeof(char);//不能在模板里边去计算,容易出问题。
//原因在于sizeof不是函数,而是一个运算符,由编译器来计算的,而不是运行的时候计算的,Arry接收的时候
//用指针接收,并不知道这个数组的长度,因此会出错
//所以说,在传数组的时候,往往还是要传入一个数组元素的个数这个参数
my_sort(Arry,length);//传实参数组的首地址
my_print(Arry,length);
int A[] = {5,1,2,3,5,7,8,9,4,6};
int length1 = sizeof(A) / sizeof(int);
my_sort(A, length1);//传实参数组的首地址
my_print(A, length1);
return 0;
system("pause");
普通模板与函数模板的区别
#include<iostream>
using namespace std;
template<typename T>
T my_swap(T a, T b)
{
return(a + b);
}
int main()
{
char a = 'a';
int b = 2;
int result = my_swap<int>(a, b);//隐式类型转换,前提是我显式的指定了T的类型(形参不要用引用)
//如果不指定int,就不可以进行隐式类型转化。如何进行类型转化就是普通函数和函数模板的区别
//普通函数自动就可以进行类型转换
return 0;
system("pause");
}
函数模板和普通函数的调用规则
1. 如果函数模板和普通函数都可以实现,优先调用普通函数
2. 可以通过空模板参数列表来强制调用函数模板
3. 函数模板也可以发生重载
4. 如果函数模板可以产生更好的匹配,优先调用函数模板
#include<iostream>
using namespace std;
void fun(int a, int b)
{
cout << "普通函数" << endl;
}
template <typename T>
void fun(T a, T b)
{
cout << "模板函数" << endl;
}
int main()
{
int a = 0, b = 5;
fun(a, b);//调普通函数或者模板函数其实都一样,这种情况下,优先调用普通函数
fun<>(a, b);//当使用空模板的时候,此时会优先调用模板函数
fun<int>(a, b);//同上,也调用模板函数
char m = 'a';
char n = 'b';
fun(m, n);//编译器偷懒,认为我调用模板函数不存在类型转换,所以优先调用模板函数
fun(a, m);//编译器优先普通函数,因为是自动类型推导,编译器认为不可发生隐式类型转换
return 0;
system("pause");
}
既然提供了函数模板,最好就不要提供重名的普通函数,否则容易出现二义性。
模板函数在自定义数据类型下的处理办法
这也是模板函数的缺点,对于识别出来T是内置的数据类型,直接进行关系运算符进行运算,如果识别出来的T不是内置的数据类型,而是我自定义的比如Person数据类型,那么进行关系运算符进行运算的时候,就会出问题,那个时候就还要进行运算符重载或者再提供一个Person的重载版本,当识别出来T是Person时候,走下面的专属于Person类的代码,具体格式见下面最后一段代码:
#include<iostream>
#include<string>
using namespace std;
class Person {
public:
Person(const string& s1, const int& age1):name(s1),age(age){}
bool operator==(Person & p2);
string name;
int age;
};
bool Person::operator==(Person & p2)
{
if (this->age == p2.age)
return true;
else
return false;
}
template <typename T>
bool compare(T& m1, T& m2)
{
if (m1 == m2)
return true;
else
return false;
}
int main()
{
int a = 0, b = 0;
cout << boolalpha <<compare(a, b) << endl;//内置的数据类型进行等号判断,OK,可以
Person p1("张三", 20), p2("李四", 20);
cout << boolalpha << compare(p1, p2) << endl;//自定义的数据类型就不可以 了,因为在16行判断不出来p1和p2是否相等
//解决办法1.是运算符重载2.是模板重载
return 0;
system("pause");
}
上面是利用运算符重载,下面是模板重载的方法
#include<iostream>
#include<string>
using namespace std;
class Person {
public:
Person(const string& s1, const int& age1):name(s1),age(age){}
string name;
int age;
};
template <typename T>
bool compare(T& m1, T& m2)
{
if (m1 == m2)
return true;
else
return false;
}
template<> bool compare(Person& m1, Person& m2)
{
if (m1.age==m2.age)
{
return true;
}
else
{
return false;
}
}
int main()
{
int a = 0, b = 0;
cout << boolalpha <<compare(a, b) << endl;//内置的数据类型进行等号判断,OK,可以
Person p1("张三", 20), p2("李四", 20);
cout << boolalpha << compare(p1, p2) << endl;//自定义的数据类型就不可以 了,因为在16行判断不出来p1和p2是否相等
//解决办法1.是运算符重载2.是模板重载
return 0;
system("pause");
}