自学内容网 自学内容网

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:
};

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


原文地址:https://blog.csdn.net/in_seattle/article/details/142531969

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!