之前学了类型模板参数,现在学习非类型模板参数
1.我们学过栈stack,知道有静态栈跟动态栈,如果用宏定义静态数组的大小,那么这个大小就固定死了,如果要变换还得手动修改宏。所以我们有了新的解决方案,那就是非类型模板参数,就是用一个常量去做模板的参数,就会变得灵活
2.STL容器里面还有个array容器,他的形参就有非类型模板参数
3.我们就会想,既然有了arr数组,为什么还要定义个array容器呢,那是因为可以检查越界读跟写
4.模板的特化:函数模板特化,类模板特化
4.1.函数模板特化:
4.1.1. 必须要先有一个基础的函数模板
4.1.2 关键字template后面接一对空的尖括号<>
4.1.3 函数名后跟一对尖括号,尖括号中指定需要特化的类型
4.1.4 函数形参表: 必须要和模板函数的基础参数类型完全相同,如果不同编译器可能会报一些奇怪的错误。
priority_queue.h
4.2类模板特化
特化的前提要写原模板,并且原模板有几个参数,特化后的模板也要一样的参数,不能多也不能少

template<class T1, class T2> 
class Data
{
public:
Data() {cout<<"Data<T1, T2>" <<endl;}
private:
T1 _d1;
T2 _d2;
};

4.2.1全特化:全特化即是将模板参数列表中所有的参数都确定化
4.2.2偏特化:任何针对模版参数进一步进行条件限制设计的特化版本
偏特化有以下两种表现方式:
部分特化
将模板参数类表中的一部分参数特化。

// 将第二个参数特化为int
template <class T1> 
class Data<T1, int>
{
public:
Data() {cout<<"Data<T1, int>" <<endl;}
private:
T1 _d1;
int _d2;
};

参数更进一步的限制
偏特化并不仅仅是指特化部分参数,而是针对模板参数更进一步的条件限制所设计出来的一个特化版本

//两个参数偏特化为指针类型 
template <typename T1, typename T2> 
class Data <T1*, T2*> 
{ 
public:
Data() {cout<<"Data<T1*, T2*>" <<endl;}
private:
T1 _d1;
T2 _d2;
};
//两个参数偏特化为引用类型
template <typename T1, typename T2>
class Data <T1&, T2&>
{
public:
Data(const T1& d1, const T2& d2)
: _d1(d1)
, _d2(d2)
{
cout<<"Data<T1&, T2&>" <<endl;
}
private:
const T1 & _d1;
const T2 & _d2;    
};
void test2 () 
{
Data<double , int> d1;      
// 调用特化的int版本
Data<int , double> d2;      
Data<int *, int*> d3;       
// 调用基础的模板    
// 调用特化的指针版本
Data<int&, int&> d4(1, 2);  // 调用特化的指针版本
}

#pragma once
template<class T>
class Less
{
public:
	bool operator()(const T& x, const T& y)
	{
		return x < y;
	}
};

template<class T>
class Greater
{
public:
	bool operator()(const T& x, const T& y)
	{
		return x > y;
	}
};

////特化
//template<>
//class Less<Date*>
//{
//public:
//	bool operator()(Date* const & x, Date* const & y)
//	{
//		return *x < *y;
//	}
//};
//
//template<>
//class Less<int*>
//{
//public:
//	bool operator()(int* const & x, int* const & y)
//	{
//		return *x < *y;
//	}
//};
//
//template<>
//class Less<char*>
//{
//public:
//	bool operator()(char* const & x, char* const & y)
//	{
//		return *x < *y;
//	}
//};

//偏特化
template<class T>
class Less<T*>
{
public:
	bool operator()(T* const & x, T* const& y)
	{
		return *x < *y;
	}
};
template<>
class Less<char*>
{
public:
	bool operator()(char* const & x, char* const & y)
	{
		return x < y;
	}
};

namespace shasha
{
	template<class T,class Container = vector<T>,class Compare = Less<T>>
	class priority_queue
	{
	public:
		priority_queue() = default;

		template<class InputIterator>
		priority_queue(InputIterator first,InputIterator last)
			:_con(first,last)
		{
			for (int i = _con.size() - 1 - 1 / 2; i >= 0; i--)
			{
				adjust_down(i);
			}
		}


		void push(const T& x)
		{
			_con.push_back(x);
			adjust_up(_con.size() - 1);
		}

		void pop()
		{
			swap(_con[0], _con[_con.size() - 1]);
			_con.pop_back();
			adjust_down(0);
		}

		const T& top()
		{
			return _con[0];
		}

		bool empty()const
		{
			return _con.empty();
		}

		size_t size()const
		{
			return _con.size();
		}




	private:            //设置成私有的原因:adjust_up 和 adjust_down 只是 priority_queue 内部维护堆结构的工具函数,不是提供给用户使用的公有接口。
		void adjust_up(int child)
		{
			Compare com;
			int parent = (child - 1) / 2;
			while (child > 0)
			{
				if(com(_con[parent], _con[child]))
				{
					swap(_con[child], _con[parent]);
					child = parent;
					parent = (child - 1) / 2;
				}

				else
				{
					break;
				}	
			}
		}

		void adjust_down(int parent)
		{
			Compare com;
			int child = parent * 2 + 1;
			while (child < _con.size())
			{
				if (child + 1 < _con.size() && com(_con[child], _con[child+1]))
				{
					child = child + 1;
				}

				if (com(_con[parent], _con[child]))
				{
					swap(_con[child], _con[parent]);
					parent = child;
					child = parent * 2 + 1;
				}

				else
				{
					break;
				}
				
			}
		}

	private:
		Container _con;
	};
}

test.cpp

#include<iostream>
#include<array>
#include<vector>

using namespace std;


//#define N 1000
//template<class T>
//class Stack       //静态栈
//{
//private:
//	T _a[N];
//	int _top;
//	int _capacity;
//};
//
//int main()
//{
//	Stack<int> s1;         //如果空间为10,那么就浪费990空间
//	Stack<int> s2;         //1000
//	return 0;
//}

//整形做非类型模板参数,size_t是整形
//template<class T = int,size_t N = 1000>
//class Stack
//{
//private:
//	T _a[N];
//	int _top;
//	int _capacity;
//};

//非整形也可以,但是要C++20才支持
//template<class T,double X>
//class B
//{};
//
//template<string str>
//class C
//{};

//int main()
//{
//	Stack<int,10> s1;
//	Stack<int,1000> s2;
//	Stack<int> s3;
//	Stack<> s4;
//
//	cout << sizeof(s1) << endl;
//	cout << sizeof(s2) << endl;
//
//	/*int x;
//	cin >> x;
//	Stack<int, x> s3;*/    //错误写法,只能传常量,不能传变量x
//	return 0;
//}

//int main()
//{
//  越界读写都可以查出来
//	array<int, 10> a1;
// 
// // 越界写抽查结束邻近位置
//	// 越界读查不了
//	int a1[10];
// 
//	a1[0] = 10;
//	a1[9] = 100;
//	//a1[10] = 100;  越界写编译器抽查结束临近位置
//	//a1[900] = 1000;   越界
//
//	//cout << a1[10] << endl;   //越界读查不了
//
//	for (auto& e : a1)
//	{
//		cout << e << " ";
//	}
//	cout << endl;
//	return 0;
//}

//int main()
//{
//	//越界读跟写都能查出来
//	array<int, 10> a1;
//	a1.fill(1);  //全部初始化1
//	vector<int> a2(10, 1);
//
//
//	for (auto& e : a1)
//	{
//		cout << e << " ";
//	}
//	cout << endl;
//
//	//10*5
//	array<array<int, 5>, 10> aa;
//}

//// 函数模板 -- 参数匹配
//template<class T>
//bool Less(T left,T right)
//{
//	return left < right;
//}
//
////特化
//template<>
//bool Less<double*>(double* left,double* right)
//{
//	return *left < *right;
//}
//
//template<>
//bool Less<string*>(string* left,string* right)
//{
//	return *left < *right;
//}
//
//int main()
//{
//	cout << Less(1, 2) << endl;
//
//	double* p1 = new double(2.2);
//	double* p2 = new double(1.1);
//	cout << Less(p1, p2) << endl;
//
//	string* p3 = new string("111");
//	string* p4 = new string("222");
//	cout << Less(p3, p4) << endl;
//
//	return 0;
//}


////上面的形参没有用引用,有点挫
//// 函数模板 -- 参数匹配
//template<class T>
//bool Less(const T& left,const T& right)
//{
//	return left < right;
//}
//
//// const 在*的左边都是修饰指针指向对象不能修改
//// const 在*的右边都是修饰指针本身
//// const T* p1   *p1
//// T const * p2  *p2
//// T* const p3    p3
//
////特化
//template<>
//bool Less<double*>(double* const & left,double* const & right)
//{
//	return *left < *right;
//}
//
//template<>
//bool Less<string*>(string* const & left,string* const & right)
//{
//	return *left < *right;
//}
//
//int main()
//{
//	cout << Less(1, 2) << endl;
//
//	double* p1 = new double(2.2);
//	double* p2 = new double(1.1);
//	cout << Less(p1, p2) << endl;
//
//	string* p3 = new string("111");
//	string* p4 = new string("222");
//	cout << Less(p3, p4) << endl;
//
//	return 0;
//}


////函数模板跟同名的普通函数可以同时存在
////// 函数模板 -- 参数匹配
//template<class T>
//bool Less(T left,T right)
//{
//	return left < right;
//}
//
//bool Less(double* left,double* right)
//{
//	return *left < *right;
//}
//
//int main()
//{
//	cout << Less(1, 2) << endl;
//
//	double* p1 = new double(2.2);
//	double* p2 = new double(1.1);
//	cout << Less(p1, p2) << endl;
//
//	return 0;
//}

//template<class T1,class T2>
//class Date
//{
//
//public:
//	Date()
//	{
//		cout << "Date(T1,T2)" << endl;
//	}
//private:
//	T1 _t1;
//	T2 _t2;
//};
//
////全特化
//template<>
//class Date<int,char>
//{
//
//public:
//	Date()
//	{
//		cout << "Date(int,char)" << endl;
//	}
//};
//
////偏特化
////特化部分参数
//template<class T1>
//class Date<T1,char>
//{
//
//public:
//	Date()
//	{
//		cout << "Date(T1,char)" << endl;
//	}
//};
//
////对参数进一步限制
//template<class T1, class T2>
//class Date<T1*,T2*>
//{
//
//public:
//	Date()
//	{
//		cout << "Date(T1*,T2*)" << endl;
//	}
//
//	void f1()
//	{
//		T1 x1;
//		cout << typeid(x1).name() << endl;
//
//		T1* x2;
//		cout << typeid(x2).name() << endl;
//	}
//};
//
//template<class T1, class T2>
//class Date<T1&,T2&>
//{
//
//public:
//	Date()
//	{
//		cout << "Date(T1&,T2&)" << endl;
//	}
//
//	void f1()
//	{
//		T1 x1;
//		
//		cout << typeid(x1).name() << endl;
//	}
//};
//
//template<class T1, class T2>
//class Date<T1*,T2&>
//{
//
//public:
//	Date()
//	{
//		cout << "Date(T1*,T2&)" << endl;
//	}
//
//	void f1()
//	{
//		T1 x1;
//		
//		cout << typeid(x1).name() << endl;
//	}
//};
//
//template<class T2>
//class Date<int,T2>
//{
//
//public:
//	Date()
//	{
//		cout << "Date(int,T2)" << endl;
//	}
//};
//
//
//
//int main()
//{
//	Date<int, int> d1;
//	Date<int, char> d2;
//	Date<int*, int*> d3;
//	d3.f1();   //int
//
//	Date<int&, int&> d4;
//	d4.f1();
//
//	Date<int*, int&> d5;
//	Date<int, int&> d6;
//
//
//	return 0;
//}

class Date
{
public:
	Date(int year = 1900, int month = 1, int day = 1)
		: _year(year)
		, _month(month)
		, _day(day)
	{}

	bool operator<(const Date& d)const
	{
		return (_year < d._year) ||
			(_year == d._year && _month < d._month) ||
			(_year == d._year && _month == d._month && _day < d._day);
	}

	bool operator>(const Date& d)const
	{
		return (_year > d._year) ||
			(_year == d._year && _month > d._month) ||
			(_year == d._year && _month == d._month && _day > d._day);
	}

	friend ostream& operator<<(ostream& _cout, const Date& d);
private:
	int _year;
	int _month;
	int _day;
};

ostream& operator<<(ostream& _cout, const Date& d)
{
	_cout << d._year << "-" << d._month << "-" << d._day;
	return _cout;
}

struct PDateLess
{
	bool operator()(const Date* p1, const Date* p2)
	{
		return *p1 < *p2;
	}
};

#include"priority_queue.h"
int main()
{
	//显示实现仿函数控制比较逻辑
	//shasha::priority_queue<Date*, vector<Date*>, PDateLess> q1;

	//缺省仿函数类,针对Date*进行特化
	shasha::priority_queue<Date*> q1;
	q1.push(new Date(2018, 10, 29));
	q1.push(new Date(2018, 10, 28));
	q1.push(new Date(2018, 10, 30));
	
	while (!q1.empty())
	{
	   cout << *q1.top() << " ";
	   q1.pop();
	}
	cout << endl;

	shasha::priority_queue<int*> q2;
	q2.push(new int(3));
	q2.push(new int(1));
	q2.push(new int(2));
	

	while (!q2.empty())
	{
		cout << *q2.top() << " ";
		q2.pop();
	}
	cout << endl;

	shasha::priority_queue<char*> q3;
	q3.push(new char('a'));
	q3.push(new char('b'));
	q3.push(new char('c'));
	

	while (!q3.empty())
	{
		cout << *q3.top() << " ";
		q3.pop();
	}
	cout << endl;


	return 0;
}

在这里插入图片描述
静态栈是直接在栈开好空间,栈一般有8M内存,相当于800万字节,如果定义静态栈就要小心那个N不能定义的太大,不然会导致栈溢出
动态栈是在堆里面开好空间,要多少就从堆里面调用出来多少,堆一般占用2G的内存
还有就是关于模板分离的问题
我们之前在写普通函数时,会把声明跟定义分开放,声明放在.h文件中,定义放在.cpp文件中,我们在测试文件时,看到声明,在链接时就会找到他的定义,call他的地址,所以没有问题
在这里插入图片描述
但是函数模板实现声明跟定义分别在不同文件就会很麻烦,因为没有显示实例化,下面那张图就可以说明
在这里插入图片描述
在这里插入图片描述
所以,就算是要声明跟定义分离的话,尽量写在同一个文件

更多推荐