C++初阶模板

Posted 蚍蜉撼树谈何易

tags:

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

模板

什么是模板?模板就类似于去浇筑东西的一个摸具,你需要什么东西,只要根据需要的不同去选择不同的浇筑材质即可。

为什么要提出模板这个概念

其实很好理解,就是为了"偷懒",就类似于工人去雕琢一个东西一样,没有模板的话雕琢起来是很慢的,但是有了模板,可以花最少的时间去雕琢出一个东西。使收益最大化。

模板的分类

分为类模板和函数模板

函数模板

函数模板概念

函数模板代表了一个函数家族该函数模板与类型无关,在使用时被参数化,根据实参类型产生函数的特定类型版本
注:模板不是一个函数,编译器根据模板生成出来的物品才可以称作是函数。

函数模板格式

template<class T>//或者 template<typename T>
T add(T a, T b)
{
	return a + b;
}

函数实例化的原理

在编译阶段,编译器会对传入的参数进行判断,进而推演出你想要生成的函数的参数及返回值,当推演可以成功的时候,此时编译器就生成推演出的代码,若生成失败(比如单参情况下,你传了一个double类型的,一个int类型的),此时编译器觉得这是摸棱两可的,此时编译器会报错。

函数模板的实例化

隐式实例化–让编译器去推演出函数参数及返回值类型

template<class T>//或者 template<typename T>
T add(T a, T b)
{
	return a + b;
}
int main()
{
	cout << add(1, 2) << endl;;
	system("pause");
	return 0;
}

在这里插入图片描述
弊端:若传入两个不同的类型,则该函数模板无法正确生成。

template<class T>//或者 template<typename T>
T add(T a, T b)
{
	return a + b;
}
int main()
{
	cout << add(1, 2.0) << endl;;
	system("pause");
	return 0;
}

在这里插入图片描述
解决办法:
1.将模板中的参数变为两个。

template<class T,class P>//或者 template<typename T>
T add(T a, P b)
{
	return a + b;
}
int main()
{
	cout << add(1, 2.0) << endl;;
	system("pause");
	return 0;
}

2.采用显式实例化,后面说

显式实例化

其实通俗点来说就是不让编译器去推演我所想要生成的是什么类型的函数,而是自己去指定生成的代码。

template<class T>//或者 template<typename T>
T add(T a, T b)
{
	return a + b;
}
int main()
{
	cout << add<int>(1, 2.0) << endl;;
	system("pause");
	return 0;
}

在这里插入图片描述

template<class T>//或者 template<typename T>
T add(T a, T b)
{
	return a + b;
}
int main()
{
	cout << add<double>(1, 2.0) << endl;;
	system("pause");
	return 0;
}

在这里插入图片描述

模板参数的匹配原则

1… 一个非模板函数可以和一个同名的函数模板同时存在,而且该函数模板还可以被实例化为这个非模板函数

// 专门处理int的加法函数
int Add(int left, int right)
{
return left + right;
}
// 通用加法函数
template<class T>
T Add(T left, T right)
{
return left + right;
}
void Test()
{
Add(1, 2); // 与非模板函数匹配,编译器不需要特化
Add<int>(1, 2); // 调用编译器特化的Add版本
}

2.对于非模板函数和同名函数模板,如果其他条件都相同,在调动时会优先调用非模板函数而不会从该模板产生出一个实例。如果模板可以产生一个具有更好匹配的函数, 那么将选择模板

// 专门处理int的加法函数
int Add(int left, int right)
{
return left + right;
}
// 通用加法函数
template<class T1, class T2>
T1 Add(T1 left, T2 right)
{
return left + right;
}
void Test()
{
Add(1, 2); // 与非函数模板类型完全匹配,不需要函数模板实例化
Add(1, 2.0); // 模板函数可以生成更加匹配的版本,编译器根据实参生成更加匹配的Add函}
  1. 模板函数不允许自动类型转换,但普通函数可以进行自动类型转换,对于类中的加法,其函数必须显式化的自己提供出来,否则会报错。
template<class T>
T add(const  T& t1, const T& t2)
{
	return t1 + t2;
}
class complex
{
public:
	complex(double real , double image ) :_real(real), _image(image)
	{

	}
	~complex()
	{

	}
	complex operator+(const complex& t1)const
	{
		complex ret(_real + t1._real, _image + t1._image);
		return ret;
	}

private:
	double _real;
	double _image;
	friend  ostream& operator<<(ostream& _cout, const complex& d);
};
ostream& operator<<(ostream& _cout, const complex& d)
{
	_cout << "实部" << d._real << "虚部" << d._image;
	return _cout;
}
int main()
{
	complex t1(0.3, 0.4);
	complex t2(0.4, 0.5);

	complex ret = t1 + t2;
	cout << ret << endl;
	return 0;
}

函数模板的重载

template<class T>
T add(T a, T b)
{
	return a + b;

}
template<class P>
P add(P a, P b, P c)
{
	return a + b + c;

}
int main()
{
	cout << add(1, 2, 3) << endl;
	cout << add(1, 2) << endl;
	return 0;
}

类模板

对于类模板,我们必须使用显式初始化来初始化生成的函数,否则会报错。不能依赖隐式实例化去推演出类型。
这里给大家两个实例吧!
在这里插入图片描述

根据类模板实现一下顺序表

//之前我们是这么写的
//typedef int datatype;//发现每次需要存储不同对象时,都得对它进行修改,显得很鸡肋,接下来看下面这种
template<class T>
class seqlist
{
public:
	seqlist(int init = 10) :arr(new T[init + 3]), size(0), capacity(init + 3)
	{

	}
	~seqlist()
	{
		if (arr)
		{
			delete[]arr;
		}
	}
	void push(const T& num)
	{
		if (is_full())
		{
			Extend();
			arr[size++] = num;
			
		}
		else
		{
			arr[size++] = num;
		}

	}
	void pop()
	{
		if (is_empty())
			size--;
		else
			return;
	}
	bool is_full()const
	{
		if (size == capacity)
		{
			return true;
		}
		else
		{
			return false;
		}
	}
	bool is_empty()const
	{
		return 0==size;
	}
	
	size_t return_size()const
	{
		return size;
	}
	size_t return_capacity()
	{
		return capacity;
	}
	const T& operator[](int index)const
	{
		if (index < 0 || index >= size)
		{
			return -1;
		}
		return arr[index];
	}
private:
	void Extend()
	{
		T* arr1 = new T[2 * capacity];
		memcpy(arr1, arr, sizeof(T) * capacity);
		delete[](arr);
		arr = arr1;
		capacity *= 2;
	}
private:
	T* arr;
	size_t size;
	size_t capacity;
};
int main()
{
	//对于类来说,必须显式实例化,不可以让它去推演
	//seqlist s(0);
	seqlist<int>s(0);
	s.push(1);
	s.push(2);
	s.push(3);
	s.push(4);
	s.push(5);
	cout << s[2] << endl;
	//用来看生成的类型的
	cout << typeid(s).name() << endl;
	seqlist<double>s1(0);
	s1.push(1.0);
	s1.push(2.0);
	s1.push(3.0);
	s1.push(4.0);
	s1.push(5.0);
	s1.push(6.1);
	cout << s1[5] << endl;
	cout << typeid(s1).name() << endl;
	return 0;

}

在这里插入图片描述

注意:这里的class seqlist是不是一个类?
答案是不是的,因为它其中用到了模板,所以说它充其量只能说是一个摸具,而根据跟摸具生成的类才是一个真正的类。比如如上面图片所看到的哪个class seqlist这才是一个真正的类。

模板类类外定义函数的写法:

template<class T>
class seqlist
{
public:
	seqlist(int init = 10) :arr(new T[init + 3]), size(0), capacity(init + 3)
	{

	}
	~seqlist()
	{
		if (arr)
		{
			delete[]arr;
		}
	}
	void push(const T& num)
	{
		if (is_full())
		{
			Extend();
			arr[size++] = num;
			
		}
		else
		{
			arr[size++] = num;
		}

	}
	void pop()
	{
		if (is_empty())
			size--;
		else
			return;
	}
	bool is_full()const
	{
		if (size == capacity)
		{
			return true;
		}
		else
		{
			return false;
		}
	}
	bool is_empty()const
	{
		return 0==size;
	}
	
	size_t return_size()const
	{
		return size;
	}
	size_t return_capacity()
	{
		return capacity;
	}
	const T&operator[](int index)const
	{
		if (index < 0 || index >= size)
		{
			return -1;
		}
		return arr[index];
	}
private:
	 void Extend();
private:
	T* arr;
	size_t size;
	size_t capacity;
};
template<class T>
void seqlist<T>::Extend()
{

		T* arr1 = new T[2 * capacity];
		memcpy(arr1, arr, sizeof(T) * capacity);
		delete[](arr);
		arr = arr1;
		capacity *= 2;
}

利用数组来初始化一个顺序表

template<class T>
class seqlist
{
public:
	seqlist(int init = 10) :arr(new T[init + 3]), size(0), capacity(init + 3)
	{

	}
	~seqlist()
	{
		if (arr)
		{
			delete[]arr;
		}
	}
	seqlist(T* first, T* last)
	{
		T* t1 = first;
		int count = 0;
		while (t1 != last)
		{
			++t1;
			++count;
		}
		arr = new T[count];
		size = count;
		capacity = count;
		for (int i = 0; i < size; i++)
		{
			arr[i] = *first;
			++first;
		}
	}
	void push(const T& num)
	{
		if (is_full())
		{
			Extend();
			arr[size++] = num;

		}
		else
		{
			arr[size++] = num;
		}

	}
	void pop()
	{
		if (is_empty())
			size--;
		else
			return;
	}
	bool is_full()const
	{
		if (size == capacity)
		{
			return true;
		}
		else
		{
			return false;
		}
	}
	bool is_empty()const
	{
		return 0 == size;
	}

	size_t return_size()const
	{
		return size;
	}
	size_t return_capacity()const
	{
		return capacity;
	}
	const T& operator[](int index)const
	{
		if (index < 0 || index >= size)
		{
			return -1;
		}
		return arr[index];
	}
private:
	void Extend();
private:
	T* arr;
	size_t size;
	size_t capacity;
};
template<class T>
void seqlist<T>::Extend(

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

C++模板初阶

C++初阶---模板入门

<c++> 四模板初阶

C++模板初阶

C++模板初阶--懒人创造世界

C++模板初阶--懒人创造世界