《C++》模板

一、本章介绍

1、泛型编程

2、函数模板

3、类模板

二、泛型编程

什么是泛型编程?不知道你有没有经历这样的情景:你需要写一个交换函数,可以交换int、double、char、自定义类型。那么这对于C语言来说,就得专门写一个int交换函数、再写一个double交换函数、再写一个char交换函数。同时C语言还不支持重载,你还得写不一样的函数名。那如果我想写一个支持任意类型的交换函数,我们就不得不学习泛型编程。简单来说,泛型编程就是编写与类型无关的通用性代码,而模板就是泛型编程的基础。

三、函数模板

先写一个交换函数的模板

template<class T>
void Swap(T& a, T& b)
{
	T temp = a;
	a = b;
	b = temp;
}

其中<class T>中的class可以换成typename,这两者在这里的使用没有区别,具体区别在迭代器那章会介绍。

需要注意的是,函数模板与具体的函数是有区别的,函数模板负责的是:你使用一个Swap<int>(a,b),它就会实例化一份Swap(int& a,int& b)。同样的,你使用一个Swap<double>(a,b),它就会实例化一份Swap(double& a,double& b),也就是说,编译器负责了跟多的工作,我们只需要传对应的参数,编译器就会依照函数模板自动实例化出你需要的函数。

3.1函数模板的实例化

函数模板的实例化分两种:

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

template<class T> 
T Add(const T& left, const T& right) 
{
	return left + right;
}
int main()
{
	int a1 = 10, a2 = 20;
	double d1 = 10.0, d2 = 20.0;
	Add(a1, a2);
	Add(d1, d2);
	return 0;
}

 像普通函数一样使用,编译器会根据你传参的类型实例化出对应的函数。

如果传的类型有矛盾,编译器会报错,如下:

template<class T> 
T Add(const T& left, const T& right) 
{
	return left + right;
}
int main()
{
	int a1 = 10, a2 = 20;
	double d1 = 10.0, d2 = 20.0;
	Add(a1, d2);
	return 0;
}

 因为a1是int类型,d2是double类型,编译器无法推出T是什么类型,如果把T实例化为nt,那么d2传过来会发生隐式类型转换,编译器不允许这样的事情发生,因此直接报错。

解决办法1:

int main()
{
	int a1 = 10, a2 = 20;
	double d1 = 10.0, d2 = 20.0;
	Add(a1, (int)d2);
	return 0;
}

解决办法2:

使用显示实例化

int main()
{
	int a1 = 10, a2 = 20;
	double d1 = 10.0, d2 = 20.0;
	Add<int>(a1, d2);
	return 0;
}
二、显式实例化:在函数名后的 <> 中指定模板参数的实际类型
int main() 
{
 int a = 1;
 double b = 1.1;
 Add<int>(a, b);
 return 0; 
}
如果类型不匹配,编译器会尝试进行隐式类型转换,如果无法转换成功编译器将会报错。
~提问环节~
问一:
如果同时存在普通函数和函数模板,使用时会选择哪个呢?
template<class T> 
void Add(const T& left, const T& right) 
{
	cout << "T Add(const T& left, const T& right)" << endl;
}

void Add(int& a, int& b)
{
	cout << "int Add(int& a, int& b)" << endl;
}

int main()
{
	int a = 0;
	int b = 5;
	Add(a, b);
	return 0;
}

 如果有现成的函数,编译器就不会实例化了,而是直接调用这个现成的函数。

问二:
下面这段代码,该输出什么?
template<class T1,class T2> 
void Add(const T1& left, const T2& right) 
{
	cout << "void Add(const T1& left, const T2& right)" << endl;
}

void Add(int& a, int& b)
{
	cout << "int Add(int& a, int& b)" << endl;
}

int main()
{
	int a = 0;
	double b = 5.5;
	Add(a, b);
	return 0;
}

 

由于模板函数可以生成更加匹配的版本,因此编译器根据实参实例化出更加匹配的Add函数。

这里再次说明一下:模板函数不允许自动类型转换,但普通函数可以进行自动类型转换

四、类模板 

我们用C语言写一个顺序表的时候,通常用改变typedef int DateType来改变存储的类型。

但如果我们想一个顺序表存int,另一个顺序表存double,这个时候我们就不得不写两个存不同类型的顺序表了,但这样写了太多重复的代码了,这对于代码人来说是不好忍受的。因此我们需要学习类模板,站在底层的角度就是让编译帮我们写重复的代码,我们只需要写一个类模板出来,想实例化存什么类型的类,直接用seqlist<int>、seqlist<double>即可。

4.1类模板的定义格式

template<class T1, class T2, ..., class Tn>
class 类模板名
{
 // 类内成员定义
};
template<class T>
class Seqlist
{

public:
	Seqlist()
	{
		//构造函数
	}
	~Seqlist()
	{
		//析构函数
	}
private:
	T* _a;
	size_t _size;
	size_t _capacity;
};

int main()
{
	Seqlist<int> s1;//存int的顺序表
	Seqlist<double> s2;//存double的顺序表
	return 0;
}

需要注意的是Seqlist不是类型,因为它还没实例化,只能说是类名,Seqlist<int>才是类型。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

李逢溪

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

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

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

打赏作者

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

抵扣说明:

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

余额充值