C++之函数模板

之前我们知道的交换两个变量的方法有宏定义、函数,这两种方式都能实现两个变量的交换,但是各有各的优缺点
    宏定义:
    - 优点:代码复用,适合所有的类型
    - 缺点:缺少类型检查,宏在预处理阶段就被替换掉,编译器并不知道宏的存在

    函数:
    - 优点:真正的函数调用,编译器对类型进行检查
    - 缺点:类型不同需要重复定义函数,代码无法复用

上边两种方式都各有利弊,但是在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");
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值