C++模板初阶

###引言:写一个交换的函数Swap,形参的类型会因为传入的实参的不同而需要改变,这样在频繁使用交换函数并且传入的实参的类型不同时会很麻烦,要写很多重载函数。而重载函数较多时会出现代码复用率较低和可维护性较低的问题;

在C++里面引入了模板的概念,模板可以用来实例化函数,让函数的形参类型根据实参的不同来生成,这样,利用一个函数模板就可以频繁操作不同类型的实参了。

模板是泛型编程的基础(泛型编程是与类型无关的通用代码,是代码复用的一种手段)

###模板分为函数模板和类模板,这里简单介绍:

函数模板

1、函数模板的引入:

以交换函数Swap为例子:

void Swap(int& x, int& y)
{
	int tmp = x;
	x = y;
	y = tmp;
}

这是基本的交换函数,只能接收int类型的实参才能正常使用;这里引入函数模板,让Swap函数可以正常操作不同类型的实参:

template<class T>
void Swap(T& x, T& y)
{
	T tmp = x;
	x = y;
	y = tmp;
}
int main()
{
	int a = 1;
	int b = 20;
	Swap(a, b);
	cout << "a = " << a << endl << "b = " << b << endl;

	double c = 1.2;
	double d = 2.22;
	Swap(c, d);
	cout << "c = " << c << endl << "d = " << d << endl;
	return 0;
}

上面代码就是使用了函数模板,让Swap能够接收不同类型的实参;

函数模板的语法规则:

template<class T1, class T2,class T3........ class T3>

函数返回类型+函数名+参数列表

 在声明一个class是来定义模板参数的关键字,也可以使用 typename (即 template<typename T1>) 

不能使用struct来定义模板参数;

函数模板时可以class和typename都使用;

2、函数模板的原理:

 函数模板是一个蓝图,它本身不是函数,而是编译器根据它来生成有具体函数参数的模板,使用模板省去了我们的重复工作。

3、函数模板参数的实例化:

1、隐式实例化:

隐式实例化:让编译器根据实参来推到函数参数的实际类型;

利用上文中Swap的函数模板来举例,在编译阶段,函数根据你传过去的实参的具体类型来确定T到底是什么,让后生成一个函数,这个函数的参数类型确定了,这就是实例化;

传入的是两个double类型的实参,那么T在函数模板生成的函数中就是double,若是其他类型,实例化之后,T就是那个传入的类型;

代码示例:

template<typename T1,typename T2>
T1 Add(T1& x, T2& y)
{
	return x + y;
}

int main()
{
	int a = 1;
	double b = 2.22;
	cout << Add(a, b) << endl;
	return 0;
}

a是int类型的,对应着T1,那么模板生成的函数里面的T1就是int,同理,T2是double类型的;

但是要注意:有时候推断会出问题:

在这个例子中函数模板只有一个定义模板参数的关键字,函数模板的形参部分都是相同的参数T,在传入时,一个类型是int,一个是double,那么编译器就不知道要把T推断成哪个类型的参数,所以就会出错;

为了解决这种问题,有两个办法:强转类型或者显示实例化;

2、显示实例化:

显示实例化:在函数名后面加个<>,<>里面写上类型,对应着函数模板里面的参数,这个参数的类型就是<>里面类型;

<>里面可以写多个要显示转换的类型;

template<class T>
T Add(T& x, T& y)
{
	return x + y;
}
int main()
{
	int a = 1;
	double b = 2.22;
	//显示实例化:
	Add<int>(a, b);
	return 0;
}

 若是类型不一样,编译器会将类型都隐式类型转换为<>里面的类型;若是转换不成功,会报错;

 4、模板参数匹配规则:

如果一个函数和一个能实例化成这个函数的函数模板同时存在,那么在调用的时候,会先直接调用函数,这是因为模板函数多了生成的一个过程,直接调用现存函数效率更高;

但是当函数模板生成的函数要优于这个函数时,会调用函数模板生成的函数;

代码示例:

int Add(int& x, int& y)
{
	return x + y;
}
template<class T>
T Add(T& x,T& y)
{
	return x+y
}

这个就是先调用函数,因为函数模板并不优于函数(示例化出来的参数不匹配)

int Add(int& x, int& y)
{
	return x + y;
}
template<class T1,class T2>
T1 Add(T1& x, T2& y)
{
	return x + y;
}

这个就会先调用函数模板,因为函数模板更优,可以生成两个不同类型的参数。


类模板:

类模板格式:

template<class T>

class +类名

{

        //类中成员的定义

}

写一个栈的类模板,让这个栈里面存储的数据可以是多种类型的数据;

#include<iostream>
using namespace std;

//类模板
template<class T>
class Stack
{
public:
	Stack(int n=4)
		:_array(new T[n])
		,_size(0)
		,_capacity(n)
	{}
	~Stack()
	{
		delete[] _array;
		_array = nullptr;
		_size = _capacity = 0;
	}
	void Push(const T& x)
	{
		if (_size == _capacity)
		{
			T* tmp = new T[2 * _capacity];
			memcpy(tmp, _array, sizeof(T) * _size);
			delete[] _array;
			_array = tmp;
			
			_capacity *= 2;
		}
		_array[_size++] = x;
	}
	void Print()
	{
		for (int i = 0; i < _size; i++)
		{
			cout << _array[i] << " ";
		}
		cout << endl;
	}
private:
	T* _array;
	int _size;
	int _capacity;
};

int main()
{
	Stack<int> st1;//类模板实例化
	st1.Push(1);
	st1.Push(2);
	st1.Push(3);
	st1.Push(4);
	st1.Push(5);
	st1.Print();

	Stack<double> st2;//类模板实例化
	st2.Push(1.1);
	st2.Push(2.1);
	st2.Push(3.1);
	st2.Push(4.1);
	st2.Push(5.1);
	st2.Print();
	return 0;
}

 

上述代码中Stack就是一个类模板,这个类模板中的数据是T类型的,在我们实例化时指定的数据类型后,T就是这个指定的数据类型;

类模板必须显示实例化,并且类模板的声明和定义不建议分离到头文件和源文件中,这样会有链接错误;

类模板不是真正的类,利用类模板实例化出来的类才是真正的类。

除此之外,类模板中的成员函数可以定义在类外面:但是要在定义的前面声明模板,并且作用域的范围是类模板Stack,Stack后面还要加上<T>,这个T实际就是类模板里面的T,但是还可以起其他名字,表示的意思还是不变的:

#include<iostream>
using namespace std;

template<class T>
class Stack
{
public:
	Stack(int n=4)
		:_array(new T[n])
		,_size(0)
		,_capacity(n)
	{}
	~Stack()
	{
		delete[] _array;
		_array = nullptr;
		_size = _capacity = 0;
	}

	void Push(const T& x);

	void Print()
	{
		for (int i = 0; i < _size; i++)
		{
			cout << _array[i] << " ";
		}
		cout << endl;
	}
private:
	T* _array;
	int _size;
	int _capacity;
};

template<class T>//还要声明,T可以换成其他字母,代表的意思相同
void Stack<T>:: Push(const T& x)//Stack<T>::指定在哪个类模板里面
{
	if (_size == _capacity)
	{
		T* tmp = new T[2 * _capacity];
		memcpy(tmp, _array, sizeof(T) * _size);
		delete[] _array;
		_array = tmp;

		_capacity *= 2;
	}
	_array[_size++] = x;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值