带你了解 C++的模板 (初阶 : 泛型编程, 函数模板, 类模板)

相信大家都自己写过交换函数 Swap( ) 吧 , 实现起来非常简单, 没有什么难度. 但是如果是做一些项目开发的时候, 有十几种甚至几十种数据类型的时候, 如果要你写十几遍甚至几十遍的 Swap() 函数, 想必大家都会疯吧~

今天要介绍的泛型编程就是解决这一问题的 ! 耐心看完, 你一定会有所收获 !

1. 泛型编程

为了解决开头说到的问题, 减轻程序猿的工作量, 大佬们就发明了泛型编程的概念. 正所谓 “世界是懒人创造的” ~

泛型编程就是引入了template关键字
后面跟着一个泛型参数, 当编译器运行到这个函数/类的时候会根据当前的数据类型去实例化生成对应类型的代码.

实际上程序的工作量是没有减轻的, 但是这无疑让我们程序猿的工作减轻了非常多, 这, 就足够了~…

接下来我们看看具体的函数模板和类模板

2. 函数模板

函数模板的定义方式 :
template<class/typename T1, class/typename T2, …>
返回值类型 函数名(参数列表){}

就拿上面说的Swap()函数来举例吧

//函数模板
template<typename T>
void Swap(T& a, T& b) {
	T tmp = b;
	b = a;
	a = tmp;
}
  • 函数模板本身不是函数, 他只是一个模具, 不是可执行的代码
  • 编译器在编译阶段会根据参数类型不同, 利用函数模板生成对应参数类型的可执行代码

下面给出测试栗子

void test() {
	int a = 1, b = 2;
	double c = 1.0, d = 2.0;
	char e = 'e', f = 'f';
	
	Swap(a, b); // 生成Swap(int, int)
	Swap(c, d); // 生成Swap(double, double)
	Swap(e, f); // 生成Swap(char, char)
}

就像注释写的那样, 我们可以通过调试时的反汇编看到函数确确实实是不一样的

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
我们可以看到, 程序在编译时调用了不同的函数, 这其实就是函数模板的实例化.

接下来我们就说说实例化

函数模板的实例化

1. 隐式实例化: 让编译器根据实参推演模板参数的实际类型

这也不用多说什么, 直接给出一个例子感受一下就知道了

template<class T>
T Add(const T& a, const T& b) {
	return a + b;
}

void test() {
	int a = 1, b = 2;
	double c = 1.0, d = 2.0;
	char e = 'e', f = 'f';

	//隐式实例化, 编译器自动推导
	Add(a, b);
	Add(c, d);
	Add(e, f);
}

直接调用就是隐式实例化

2. 显式实例化:在函数名后的<>中指定模板参数的实际类型

template<class T>
T Add(const T& a, const T& b) {
	return a + b;
}

void test() {
	int a = 1, b = 2;
	double c = 1.0, d = 2.0;
	char e = 'e', f = 'f';

	//强制类型转换 --> 不推荐
	Add(a, (int)d);
	
	//显式实例化: 函数<实例化类型>(参数列表) --> 推荐
	Add<int>(a, d);
}

如果类型不匹配,编译器会尝试进行隐式类型转换,如果无法转换成功编译器将会报错。

模板参数的匹配规则

1. 函数模板和普通函数同时出现, 并且普通函数的参数可以完全匹配, 那么编译器会调用普通函数, 不会进行函数模板的实例化

毕竟有现成的, 编译器也很懒丫~

2. 如果函数模板和普通函数共存, 用函数模板可以生成参数类型更加匹配的函数, 则会进行实例化(更加精确)

也就是普通函数如果不是完全匹配的话, 还是会使用函数模板进行实例化生成最匹配的函数

这没什么问题吧…很好理解的…

3. 如果手动指明要显式实例化, 则进行实例化, 不会调用普通函数

下面给出具体的例子

template<class T>
T Add(const T& a, const T& b) {
	return a + b;
}

//普通函数和函数模板共存
int Add(int& a, int& b) {
	return a + b;
}

void test2() {
	int a = 1, b = 2;
	
	//如果参数类型和普通函数完全匹配, 则优先调用普通函数, 不会进行函数模板的实例化
	Add(a, b);

	double c = 1.0;
	//如果函数模板可以生成参数类型更加匹配的函数, 则会执行实例化 (更精确)
	Add(a, c);

	//如果指明要显式实例化, 则要执行实例化, 不会调用普通函数
	Add<int>(a, b);
}

3. 类模板

1. 定义方式

与函数模板类似, 在类前面加上
template<class T1, class T2, …, class Tn>

template<class T>
struct seqList {
	T* _array;
	size_t _size = 0;
	size_t _capacity = 10;

	seqList(size_t num = 10)
		:_array(new T[num])
	{}

	void push_back(T x) {
		_array[_size++] = x;
	}
};

2. 类模板的实例化

类模板实例化与函数模板实例化不同,类模板实例化需要在类模板名字后跟<>,然后将实例化的类型放在<> 中即可,类模板名字不是真正的类,而实例化的结果才是真正的类。

类模板必须显式实例化
类型: 类名<参数类型> ---> 类型 != 类名

下面给出例子

void test3() {

	//实例化必须为显示实例化
	seqList<int> sq;
	seqList<char> sq2;
	seqList<double> sq3;

	sq.push_back(1);
	sq.push_back(2);
	sq.push_back(3);

	sq2.push_back('a');
	sq2.push_back('b');
	sq2.push_back('c');

	sq3.push_back(1.0);
	sq3.push_back(2.0);
	sq3.push_back(3.0);
	  
}

到这里就结束了, 这里只对模板进行简单的介绍, 只要能把模板使用起来就好, 后面会有更详细的解读~

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

殇&璃

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值