当前位置 博文首页 > yumoz:C++模板(函数模板、类模板)

    yumoz:C++模板(函数模板、类模板)

    作者:[db:作者] 时间:2021-07-14 15:41

    为什么要有模板?

    如果要交换两个值,如果两位数都是(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;
    
    cs