《C++ 模板进阶》
之前学了类型模板参数,现在学习非类型模板参数
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他的地址,所以没有问题
但是函数模板实现声明跟定义分别在不同文件就会很麻烦,因为没有显示实例化,下面那张图就可以说明

所以,就算是要声明跟定义分离的话,尽量写在同一个文件
更多推荐

所有评论(0)