【C++】泛型编程 _函数模板和类模板的基本使用

1.泛型编程

如何实现一个通用的交换函数?这在C语言中是无法实现的,但是C++却可以。
C语言一次只能实现一个类型的交换:

void Swap(int& left, int& right)
{
    
    
	int temp = left;
	left = right;
	right = temp;
}
void Swap(double& left, double& right)
{
    
    
	double temp = left;
	left = right;
	right = temp;
}
void Swap(char& left, char& right)
{
    
    
	char temp = left;
	left = right;
	right = temp;
}

使用函数重载虽然可以实现,但是有一些问题:只要交换的类型不同,就需要增加对应的函数;重载的函数只是类型不同,代码复用率比较低;代码可维护性较低,一个函数出错所有重载都会出错。
解决这些问题的简单方法就是使用泛型编程。
泛型编程:编写与类型无关的通用代码,从而实现代码的复用。
像古代的活字印刷术,只要有了一个模板,就可以反复的造轮子;
在这里插入图片描述
C++中的模板可以分为函数模板和类模板。

2.函数模板

2.1 函数模板概念

函数模板代表了一个函数家族,该函数模板与类型无关,在使用时被参数化,根据实参类型产生函数的特定类型版本。

2.2函数模板格式

template<typename T1, typename T2,......,typename Tn>
返回值类型 函数名(参数列表){
    
    }

由于函数模板的模板参数类型可以有多个,所以这里使用T1, T2 … Tn来标识。
typename后面的类型名T1,T2……是随便取的,但是要尽量做到见名知意,一般首字母大写。
使用函数模板定义一个通用的交换函数:

template<typename T>
void Swap( T& left, T& right)
{
    
    
	T temp = left;
	left = right;
	right = temp;
}

注意:typename是用来定义模板参数关键字,也可以使用class(切记:不能使用struct代替class)。

2.3 函数模板的原理

在这里插入图片描述
下面从汇编语言看一下,当不同类型的变量调用Swap函数的时候调用的是不是同一份:
在这里插入图片描述
不同的类型编译器会推演出不同的函数,调用的也就是不同的函数。
交换函数使用的频率还是很高的,C++库(在std命名空间中)中有一个交换函数swap是可以直接使用的。

2.4 函数模板的实例化

用不同类型的参数使用函数模板时,称为函数模板的实例化。模板参数实例化分为:隐式实例化和显式实例
化。
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);
	//Add(a1, d1);  编译报错
	return 0;
}

在编译期间,当编译器看到该实例化时,需要推演其实参类型通过实参a1将T推演为int,通过实参d1将T推演为double类型,但模板参数列表中只有一个T,编译器无法确定此处到底该将T确定为int 或者 double类型而报错。
2. 显式实例化:在函数名后的<>中指定模板参数的实际类型。

int main()
{
    
    
	int a = 10;
	double b = 20.0;
	Add<int>(a, b);
	return 0;
}

2.5 模板参数的匹配原则

模板函数使得我们的代码更简洁,但是也有着匹配规则,下面看这样一段代码:

int Add(int left, int right)
{
    
    
	return left + right;
}

template<class T>
T Add(T left, T right)
{
    
    
	return left + right;
}

template<class T1, class T2>
T1 Add(T1 left, T2 right)
{
    
    
	return left + right;
}

两个函数模板同时出现,还有一个处理int类型的函数,下面看一下调用规则:

Add(1, 2); // 与非模板函数匹配,编译器不需要实例化模板,直接调用
Add<int>(1, 2); // 显示实例化
Add(1, 2.0);//需要实例化两个类型参数的模板
Add(1.0, 2.0); //优先实例化一个类型参数的模板

3.类模板

相比于函数模板,类模板并没有特殊的地方,只是类模板只能显示实例化
下面以一个栈(Stack)为例:

typedef int STDataType;
class Stack
{
    
    
private:
	STDataType* _a;
	int top;
	int capacity;
};

数据类型是使用typedef进行重命名的,那么要想同时存储两种数据类型,还需要写一份完全一样的代码,只是类型不同;这样的话就很麻烦,所以使用到了类模板。

template<class T>
class Stack
{
    
    
private:
	T* _a;
	int top;
	int capacity;
};

这样编译器就可以根据不同的类型进行显示实例化。

int main()
{
    
    
	Stack<int> s1;
	Stack<char> s2;
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_63179783/article/details/128364974