C++模板(函数模板类模板)

Posted yumoz

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C++模板(函数模板类模板)相关的知识,希望对你有一定的参考价值。

为什么要有模板?

如果要交换两个值,如果两位数都是(int,int)则需要调用下面第一个函数void Swap(int &left, int &right),如果要交换的是(double,double)则要调用的是void Swap(double &left, double &right)。那么如果需要调用别的float,long等类型就需要在此实现函数重载,虽然也可以实现,但是相对代码的复用率比较低,代码维护性也比较低效。于是就有了泛函编程

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;
}

泛函编程:编写与类型没有关系的通用代码,是代码复用的一种手段。模板作为泛函编程的基础。对于模板有以下两种,函数模板和类模板。

函数模板

函数模板是一个函数家族,该函数模板与类型无关,在使用过程中被参数化,根据调用的实参类型产生函数的特定类型的版本。

函数模板的格式

template <typename T1, typename T2, ... , typename Tn>
返回值类型 函数名 (参数列表){}
这里将上述的交换函数写成函数模板为下面形式:

template<typename T> //定义模板参数T可使用typename或class
void Swap(T& left, T& right)
{
	T temp = left;
	left = right;
	right = temp;
}

注释:typename可以换成class 但是 class不能换成struct。

函数模板原理

函数模板本质不是函数,是编译器用使用方式产生特定具体类型函数的模具。模板也就是说将我们该做的重复的事交给编译器去完成。
查看下图,仔细分析发现调用函数的模板实现交换,但是调用函数的地址不一样,可以说明低层调用的是不同的函数。
在这里插入图片描述
总结:
在编译器阶段,对于模板函数的使用,编译器是依据传入的实参类型来推演出对应类型的函数以供调用。(当我们使用int类型去调用函数模板时,编译器根据实参类型推演,将T确定为int类型,然后产生一份用来处理int类型的代码,就如上图call不同的地址的函数)。

函数模板实例化

函数模板实例化:是用不用类型的参数使用函数模板。
模板参数实例化:

  1. 隐式实例化
  2. 显示实例化。

隐式实例化:
(编译器根据实参推演模板参数的实际类型)
显示实例化:
(语法:函数名<指定的类型>(实参列表) 。)
在这里插入图片描述

模板参数匹配

  1. 一个非模板函数可以和函数模板同时存在,该函数模板仍然可以被实例化为这个非模板函数。
  2. 对于非模板函数和函数模板同时存在时,在同样情况下,一般情况下,优先选择调用非模板函数,而不用选用函数模板来去实例化一个函数,除非此函数模板可以实例化出来更好的匹配的函数。
    简述:有现成的匹配的函数,就调用现成的。实例化的更合适就选择匹配实例化出来的函数。
  3. 函数模板不允许自动类型的转换,这点普通函数可以实现自动类型转换。

类模板

定义格式

template <class T1,class T2,...,class Tn>
class 类模板名
{
	//类内的成员函数和成员变量
} 

类模板实现

namespace yumoz
{
	template <class T>
	class vector
	{
	public:
		vector()
			:_a(nullptr)
			, _size(0)
			, _capacity(0)
		{}

		~vector()
		{
			delete[] _a;
			_a = nullptr;
			_size = _capacity = 0;
		}

		void push_back(const T& x)
		{
			if (_size == _capacity)
			{
				int newcapacity = _capacity == 0 ? 4 : _capacity * 2;
				T* temp = new T[newcapacity];
				if (_a)
				{
					memcpy(temp, _a, sizeof(T)*_size);
					delete[] _a;
				}
				_a = temp;
				_capacity = newcapacity;
			}
			_a[_size] = x;
			++_size;
		}

		//类内实现
	/*	T& operator[](size_t pos)
		{
			assert(pos < _size);
			return _a[pos];
		}
		size_t size()
		{
			return _size;
		}*/

		//类外实现 部分1
		T& operator[](size_t pos);
		size_t size();

	private:
		T* _a;
		size_t _size;
		size_t _capacity;
	};

	//类外实现  部分2
	template<class T>//不可省略
	T& vector<T>::operator[](size_t pos)
	{
		assert(pos < _size);
		return _a[pos];
	}
	template<class T>//不可省略
	size_t vector<T>::size()
	{
		return _size;
	}
}

int main()
{
	yumoz::vector<int>v1;
	v1.push_back(1);
	v1.push_back(2);
	v1.push_back(3);
	v1.push_back(4);

	//v1.operator[](3)
	for (size_t i = 0; i < v1.size(); ++i)
	{
		v1[i] *= 3;//扩大三倍,返回值是引用
	}

	for (size_t i = 0; i < v1.size(); ++i)
	{
		cout << v1[i] << " ";
	}
	cout << endl;

	yumoz::vector<double>v2;
	v2.push_back(1.1);
	v2.push_back(2.2);
	for (size_t i = 0; i < v2.size(); ++i)
	{
		cout << v2[i] << " ";
	}
	cout << endl;

	return 0;
}

类模板实例化

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

vector<int>v1;
vector<double>v2;

以上是关于C++模板(函数模板类模板)的主要内容,如果未能解决你的问题,请参考以下文章

C++——初识模板

C++模板编程入门模板介绍模板定义函数模板类模板模板的继承

C++入门C++ 函数模板&类模板

C++ 模板 (Template)

C++模板

C++模板详解