C++系列-模版

????个人主页:羽晨同学 

????个人格言:“成为自己未来的主人~”  

非类型模版参数

模板参数分类型模板参数与非类型模板参数

类型形参即:出现在模板参数列表,跟在class或者typename之类的参数类型名称

非类型形参即:就是用一个常量作为类(函数)模版的一个参数,在类(函数)模版中可将该参数当成常量来使用

int main()
{
	Stack<int> st1;     //10
	Stack<int,100> st1;     //100
	Stack<int,1000> st1;     //1000

	return 0;
}

首先,我们想在定义的类中实现这样的功能,很明显,这是不可能的,但是有了非类型模板参数之后,这就成为了可能,那应该怎么做呢?

#define _CRT_SECURE_NO_WARNINGS 
#include<stack>
#include<iostream>
using namespace std;
template<class T,size_t N=10>
class Stack
{
public:
	void func()
	{
		N++;
	}
private:
	int _a[N];
	int _top;
};
int main()
{
	Stack<int> st1;     //10
	Stack<int,100> st1;     //100
	Stack<int,1000> st1;     //1000

	return 0;
}

你看, 这样的话,我们就可以通过改变传递的参数来改变N的值

需要注意的是,C++20之前,浮点数是不允许作为非类型模版参数的

但是在C++20当中,它是可以的。

template<double X,int*ptr>
class A
{};

但是类对象和字符串是不允许的

 array

array相较于vector而言,其实它具备更严格的越界检查。

#include<array>
#include<vector>
int main()
{
	//严格越界检查
	array<int, 10> aa1;
	cout << sizeof(aa1) << endl;
	aa1[10];
	aa1[14] = 1;

	array不如用vector。鸡肋的设计
	//vector<int> v1(10, 1);
	//v1[14];
	//cout << sizeof(v1) << endl;

	return 0;
}

这里其实就体现出来了一部分array的特征

	int aa2[10];
	aa2[13];

你看,在这个代码中,就不会进行报错的,这是因为在静态变量中, 执行的是随机抽样检测,所以后面是不会报错的。

我们再来看一下vector的越界部分,在vector当中,同样也会进行严格的越界检查,溢出也会进行报错。

但是,array所花费的内存比vector更大:

所以说,其实array是完全鸡肋的设计

你看,下面的这段代码是我们所实现的一个打印int的打印函数

void PrintVector(const vector<int>& v)
{
	vector<int>::const_iterator it = v.begin();
	while (it != v.end())
	{
		cout << *it << " ";
		++it;
	}
	cout << endl;
}

在这个代码中,我们所能打印的只是int类型的变量,但是我们想要打印其他的类型,我们应该怎么做呢,最简单的,引入模版的概念。

 我们来看下面的这一段代码:

template<class T>
void PrintVector(const vector<T>& v)
{
	vector<T>::const_iterator it = v.begin();
	while (it != v.end())
	{
		cout << *it << endl;
		++it;
	}
	cout << endl;
}
int main()
{
	vector<double> v2{1.1, 2.2, 1.3, 6.6, 7.7, 8.8};
	PrintVector(v2);

	return 0;
}

但是很抱歉的是,这样代码是会进行报错的,这是为什么呢?

这是因为类模板在没有实例化之前,是不会去里面查具体的东西的,这就导致了编译器不知道这个是vector<T>::const_iterator it类型还是变量 。

 而在这个时候,我们就会在代码的前面加一个typename来告诉编译器这是一个类型

template<class T>
void PrintVector(const vector<T>& v)
{
	typename vector<T>::const_iterator it = v.begin();
	while (it != v.end())
	{
		cout << *it << endl;
		++it;
	}
	cout << endl;
}
int main()
{
	vector<double> v2{1.1, 2.2, 1.3, 6.6, 7.7, 8.8};
	PrintVector(v2);

	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;
}
//template<class T>
//bool Less(const T& left, const T& right)
//{
//	return left < right;
//}
bool Less(Date* left, Date* right)
{
	return *left < *right;
}

你看,在这段代码中,我们实现了一个小于的比较,这里能够实现是因为我们对这个<进行的函数重载。

通常情况下,我们使用模版可以实现一些与类型无关的代码,但对于一些特殊类型的可能会得到一些错误的结果,需要特殊化处理。

template<class T>
bool Less( T left,  T right)
{
	return left < right;
}
int main()
{
	cout << Less(1, 2) << endl;
	Date d1(2022, 7, 7);
	Date d2(2022, 6, 8);
	cout << Less(d1, d2) << endl;
	Date* p1 = &d1;
	Date* p2 = &d2;
	cout << Less(p1, p2) << endl;

	return 0;
}

 在这个代码当中:

我们可以看到,第三个答案是错误的。因为这个是地址的比较

此时,我们就需要对模版进行特化,即:在原模板基础上,针对特殊类型进行特殊化的处理方式。模板特化中,分为类模板特化和函数模板特化

//类模板
template<class T1,class T2>
class Data
{
public:
	Data()
	{
		cout << "Data<T1,T2>-原模板" << endl;
	}
private:
	T1 _d1;
	T2 _d2;
};
//特化:针对某种特殊类型,进行特殊化处理
//全特化
template<>
class Data<int,char>
{
public:
	Data()
	{
		cout << "class Data<int,char>-> 全特化 " << endl;
	}
};
template<class T1>
class Data<T1, int>
{
public:
	Data()
	{
		cout << "class Data<T1, int>->半特化" << endl;
	}
private:
	T1 _d1;
	int _d2;
};

上面的这个其实给出了全特化和半特化的例子,半特化中我们引出了参数,一个固定,一个随机参数。 

限定模版类型

// 限定模版的类型
template <typename T1, typename T2>
class Data <T1*, T2*>
{
public:
	Data() { 
		cout << typeid(T1).name() << endl;
		cout << typeid(T2).name() << endl;
		cout << "Data<T1*, T2*>-偏特化" << endl << endl;
		//T1 x1;
		//T1* p1;
	}
};

template <typename T1, typename T2>
class Data <T1&, T2&>
{
public:
	Data()
	{
		cout << typeid(T1).name() << endl;
		cout << typeid(T2).name() << endl;
		cout << "Data<T1&, T2&>" << endl << endl;
	}
private:
};

template <typename T1, typename T2>
class Data <T1&, T2*>
{
public:
	Data()
	{
		cout << typeid(T1).name() << endl;
		cout << typeid(T2).name() << endl;
		cout << "Data<T1&, T2*>" << endl << endl;
	}
private:
};

好了,本次的文章就到这里了,我们下次再见。  

上一篇:汽车信息安全 -- 再谈车规MCU的安全启动


下一篇:DC00022基于ssm高校社团管理系统web社团管理系统java web+MySQL项目web程序设计