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()
{

	T* arr1 = new T[2 * capacity];
	memcpy(arr1, arr, sizeof(T) * capacity);
	delete[](arr);
	arr = arr1;
	capacity *= 2;
}
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;
	int arr[6] = { 1,2,3,4,5,6 };
	const seqlist<int>s3(arr, arr + sizeof(arr) / sizeof(arr[0]));
	cout << s3.return_size() << endl;
	cout << s3.return_capacity() << endl;
	cout << s3[4] << endl;
	cout << typeid(s3).name() << endl;
	return 0;

}

给大家一点小建议:C++的一大特性便是封装,是为了保证数据的安全性,所以建议大家在不需要对其中某种元素做出修改时,尽量给将其变为const成员函数,保护自己的数据安全性。还有尽量在C++中能返回引用尽量返回引用,因为返回引用的话相对于一个类来说,可以少调用一次拷贝构造函数。

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值