C++ 模板: 函数模板

C++ 模板 函数模板

1. 模板的概念

模板就是建立通用的模具,大大提高复用性

生活中的模板

  1. 一寸照片模板 可以将自己头像P到图上
    在这里插入图片描述
  2. PPT模板
    在这里插入图片描述

模板注意的点:

• 模板不能直接使用,只是一个框架需要把内容填充好
• 模板也不是万能的,是对应一类事情的模板


2. 函数模板

• C++ 的另一种编程思想成为泛型编程,主要利用的技术就是模板
• C++提供了两种模板机制: 函数模板和类模板

2.1 函数模板语法

函数模板的作用:

建立一个通用函数,其函数返回值类型和形参类型可以不具体定制,用一个虚拟的类型来表示

语法

template<typename T>
函数声明或定义

比如:
int func(int a );
我们不指定返回值 int 和 形参的类型 int
用虚拟的类型T来代表它
T func(T a)
到使用的时候才来确定它

解释:

  1. template: 声明创建模板
  2. typename: (类型名称)表明其后面的符号是一种数据类型,可以用class代替
  3. T: 通用的数据类型,字母名称可以替换,通常大写;类型参数化

示例

void swapInt(int& a, int& b)
{
	int tmp;
	tmp = a;
	a = b;
	b = tmp;
}

void swapFloat(float& a, float& b)
{
	float tmp;
	tmp = a;
	a = b;
	b = tmp;
}

template<typename T> //声明一个模板,告诉编译器后面的代码中紧跟着T的不要报错,T是一个通用数据类型
void swapT(T& a, T& b)
{
	T tmp;
	tmp = a;
	a = b;
	b = tmp;
}

void test()
{
	int a = 10; 
	int b = 20;
	//swapInt(a, b);
	//两种方式使用函数模板
	//1. 自动类型推导
	swapT(a, b);
	cout << "a = " << a << "\nb = " << b << endl;

	float c = 2.1;
	float d = 4.3;
	//swapFloat(c, d);
	//2. 显示指定类型
	swapT<float>(c, d);
	cout << "c = " << c << "\nd = " << d << endl;
}

总结:
• 函数模板利用关键字 template
• 使用函数模板有两种方式: 自动类型推导,显示指定类型
• 模板的使用是为了提高复用性,将类型参数化

2.2 函数模板注意事项

注意事项:
• 自动类型推导,必须推导出一致的数据类型T,才可以使用
• 模板必须要确定出T的数据类型,才可以使用

示例

template<typename T> //声明一个模板,告诉编译器后面的代码中紧跟着T的不要报错,T是一个通用数据类型
void swapT(T& a, T& b)
{
	T tmp;
	tmp = a;
	a = b;
	b = tmp;
}

void test()
{
	int a = 10; 
	int b = 20;
	char c = 'c';
	//1. 自动类型推导,必须推导出一致的数据类型T才可以使用
	//swapT(a, c); 错误 类型不一致,推导不出来
	cout << "a = " << a << "\nb = " << b << endl;
}

//2. 模板必须要确定出T的类型,才可以使用
template<class T>
void func()
{
	cout << "func" << endl;
}

void test01()
{
	func<int>();//确定的数据类型
}

总结:
• 使用模板必须确定出通用的数据类型T, 并且能够推导出一致的类型

2.3 函数模板案例

案例描述:
• 利用函数模板封装一个排序的函数,可以对不同数据类型数组进行排序
• 排序规则从大到小,排序算法为选择排序
• 分别使用char数组和int数组进行测试

template<typename T>
void swapT(T &a, T &b) //交换函数模板
{
	T tmp;
	tmp = a;
	a = b;
	b = tmp;
}

template<typename T>
void printArr(T arr[], int len) //打印数组函数模板
{
	for (int i = 0; i < len; i++)
	{
		cout << arr[i] << " ";
	}
	cout << endl;
}

template<typename T>
void sortArr(T arr[], int len) //数组排序函数模板
{
	for (int i = 0; i < len; i++)
	{
		int max = i;
		for (int j = i + 1; j < len; j++) 
		{
			if (arr[max] < arr[j]) //选择排序
			{
				max = j;
			}
		}
		if (max != i)
		{
			swapT(arr[max], arr[i]);
		}
	}
}

void test()
{
	char arr[] = "acefb";
	int len = sizeof(arr) / sizeof(char);
	sortArr(arr, len);
	printArr(arr, len);

	cout << "**********************" << endl;
	int arr1[] = { 1, 6, 3, 8, 32 };
	int len1 = sizeof(arr1) / sizeof(int);
	sortArr(arr1, len1);
	printArr(arr1, len1);
}

在这里插入图片描述

2.4 普通函数与函数模板的区别

主要就是可不可以发生隐式类型转换

• 普通函数调用时可以发生自动类型转换(隐式类型转换)
• 函数模板调用时,如果利用自动类型推导,不会发生隐式类型转换
• 如果利用显示指定类型方式,可以发生隐式类型转换

示例

//1. 普通函数调用可以发生隐式类型转换
//2. 函数模板 用自动类型推导,不可以发生隐式类型转换
//3. 函数模板 用显示指定类型,可以发生隐式类型转换
int  add(int a, int b)
{
	return a + b;
}

template<typename T>
T myAdd(T a, T b)
{
	return a + b;
}

void test()
{
	int a = 10; 
	int b = 20;
	char c = 'a'; //97
	
	cout <<"普通函数" <<add(a, c) << endl;
	//cout << "函数模板 自动类型" << myAdd(a, c) << endl; //没有与参数列表匹配的模板
	cout << "函数模板指定类型" << myAdd<int>(a, c) << endl;
}

总结:

  • 建议使用显示指定类型的方式,调用函数模板,因为可以自己确定通用类型T

2.5 普通函数与函数模板的调用规则

调用规则:

  1. 如果函数模板和普通函数都可以实现,优先调用普通函数
  2. 可以通过空模板参数列表来强调调用了函数模板
  3. 函数模板可以发生重载
  4. 如果函数模板可以产生更好的匹配,优先调用函数模板
//1. 如果普通函数和函数模板都可以调用,优先调用普通模板
//2.可以通过空模板参数列表 强制调用函数模板
//3. 函数模板可以发生函数重载
//4. 函数模板可以产生更好的匹配,优先使用函数模板

void func(int a, int b)
{
	cout << "调用普通函数" << endl;
}

template<typename T>
void func(T a, T b)
{
	cout << "调用函数模板" << endl;
}
//函数模板的重载
template<typename T>
void func(T a, T b, T c)
{
	cout << "调用函数模板的重载" << endl;
}

void test()
{
	int a = 10; 
	int b = 10;
	int c = 10;
	char d = 'a';
	char e = 'b';
	cout << "1." << endl;
	func(a, b);
	cout << "2." << endl;
	func<>(a, b);
	cout << "3. " << endl;
	func(a, b, c);
	cout << "4. " << endl;
	func(d, e);
}

在这里插入图片描述
总结:

  • 既然提供了函数模板,最好不要提供普通函数,否则容易出现二义性

2.6 模板的局限性

• 模板的通用不是万能的

例如:

template<class T>
void f(T a, T b)
{
    a = b;
}

上述代码中提供的赋值操作,如果传入的a和b是一个数组,就无法实现了

再例如:

template<class T>
void f(T a, T b)
{
    if(a > b){...}
}

上述代码中,如果T类型传入的是像Person这样的自定义数据类型,就无法正常运行

因此C++为了解决这种问题,提供了模板的重载,可以为这些特定的类型提供具体化的模板

class Person {
public:
	Person(string name, int age)
	{
		m_Name = name;
		m_Age = age;
	}
	string m_Name;
	int m_Age;
};

template<typename T>
bool comparePerson(T& p1, T& p2)
{
	if (p1 == p2)
		return true;
	else
		return false;
}

//来提供Person 类型的函数模板
template<> bool comparePerson(Person& p1, Person& p2)
{
	if (p1.m_Age == p2.m_Age && p1.m_Name == p2.m_Name)
		return true;
	else
		return false;
}
void test()
{
	Person p1("tom", 11);
	Person p2("tom", 21);
	bool b = comparePerson(p1, p2);
	if(b)
		cout << "p1 == p2" << endl;
	else
		cout << "p1 != p2" << endl;
}

总结:
• 利用具体化的模板,可以实现自定义类型的通用化
• 学习模板不是为了写模板,而是在STL能够运用系统提供的模板

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值