如何给一个类增加迭代器:
1.根据容器底层的数据结构封装一个迭代器类 实际上是对指针进行封装 构造函数 / * / -> / == / ++ / --(非必须,因为单链表就不需要) / !=,在迭代器类中给出这些操作, 2.在容器类中给迭代器类型取别名;其实不取也可以,取的话就会显得统一:typedef迭代器类型 iterator; 3.在容器类中给出begin()和end()操作接口 所以算法就可以通过迭代器透明化(不必知道容器底层的数据结构)的去操作容器中的数据 迭代器分类: 原生态的指针 对原生态指针的封装 关于迭代器失效: 迭代器失效:迭代器对应的指针失效,指针为野指针,指针指向的 内容不存在 解决的办法就是给迭代器重新赋值 vector和list失效的场景?前面说过了 算法可以定制功能: 仿函数(函数对象):可以将一个类的对象按照函数的方式进行使用。仿函数的功能就是可以使算法,或者一个类更加的灵活。 在仿函数中就是定义一个类,在类中对()进行重载, 比如有一个算法for_ward(v.begin(),v.end(),op)意思就是在v的这段区间上的每一个元素,进行op 操作,op就是你所要进行的操作,可以是一个仿函数,为了定制这个函数的功能,你可以先定义一个类, 在类中对()重载,在函数体内是你要对每个元素进行的操作。 适配器: 比如我们平时用的stack和queue,就是对deque的封装 priority_queue:堆(默认大堆)想要使用小堆就要<存放的元素类型,vector<存放的元素类型>,great<存放的元素类型>> 注意:优先级队列中存放的必须是内置类型的话优先级队列可以直接使用 加入存放的是自定义的数据类型则必须要对>或者<进行重载,但是有的时候重载之后还是不行,那么可能就需要通过仿函数来进行完成了C++适配器
模板参数分为:非类型模板参数,类型模板参数
template<class T,size_t N> 这个T就是类型模板参数,N就是非类型的模板参数 class array { public: ... private: T _array[N]; //N是常量 size_t _size; }; array<int,10> arr; //定义类型的时候必须要都给出,例如这个还要指定大小 注意: 1.浮点数,类对象以及字符串是不允许作为非类型的模板参数的(自定义的类型也不可以) 2.非类型的模板参数必须要求在编译期就能确定结果,否则就是失败模板的特化:
#include<iostream> using namespace std; template<class T> T& MAX(T& left,T& right) { return left > right ? left : right; } class Date { friend ostream& operator<<(ostream& _cout, Date& p); public: Date(int year,int month,int day) :_year(year) , _month(month) , _day(day) {} bool operator>(Date& p) { if (_year > p._year || _year == p._year && _month > p._month || _year == p._year && _month == p._month&&_day > p._day) return true; return false; } private: int _year; int _month; int _day; }; ostream& operator<<(ostream& _cout, Date& p) { _cout << p._year << " " << p._month << " " << p._day << " "; return _cout; } int main() { int a = 10; int b = 20; cout << MAX(a, b) << endl; //内置类型可以直接比较 cout << MAX(b, a) << endl; Date d1(2019, 10, 22); //自定义类型需要在类中将比较方式给出,那么就可以使用MAX函数来进行比较了 Date d2(2019, 10, 23); cout << MAX(d1, d2) << endl; cout << MAX(d2, d1) << endl; //但是有的类型不能处理,或者处理出来就是错误 //通常情况下可以使用模板实现一些与类型无关的代码,但是对于一些特殊类型得到的就是错误的情况 char* p1 = "world"; char* p2 = "hello"; cout << MAX(p1, p2) << endl; //打印结果出错 cout << MAX(p2, p1) << endl; return 0; }可以看出对于char*类型的变量的比较是存在错误的
模板的特化又分为函数模板的特化和类模板的特化: 函数模板一般特化的比较少 对于上面的函数来说就是MAX函数对于char*类型的变量在比较的时候会存在错误,所以接着我们就对char*类型来进行特化 在上面的代码段中插入下面的代码,那么在调用char*类型的函数的时候就会自动的去调用我们特化的MAX函数 //模板的特化 template<> char*& MAX<char*>(char*& left, char*& right) { if (strcmp(left, right) == 1) ///strcmp大于返回1,小于返回-1 return left; return right; }特化后的打印结果: 函数模板特化的注意事项:
如果要进行特化,那么首先就得先有一个模板函数(例如上面第一次的MAX函数),其次是我们要清楚,已经给出的模板对于哪种类型是有问题的(比如char*),这样才知道要对哪种类型,进行特化。 //普通模板和特化模板的比较 template<class T> T& MAX(T& left,T& right) { return left > right ? left : right; } template<> char*& MAX<char*>(char*& left, char*& right) { if (strcmp(left, right) == 1) ///strcmp大于返回1,小于返回-1 return left; return right; }但是一般函数模板是不需要特化的,我们对于上面的情况不是进行特化,而是直接将不能处理类的函数直接给出(例如直接将char*类型的比较函数给出),因为直接给出的话比较简单,容易,因为某些函数的特化很难搞定,或者说根本搞不定。
char* MAX(char* left,char* right) { if (strcmp(left, right) == 1) ///strcmp大于返回1,小于返回-1 return left; return right; }反正就是函数特化,有时候容易出状况,就算你把特化版本写出来有时也不会调,所以说函数模板对于哪一种类型有问题,就直接把哪一种类型的具体函数给出来就可以了,特化容易出错。
2》 void func(const T& p) //那么按照常理来推测就是const int*& p ,也就是说const修饰的是p所只想的空间里面的内容不能修改 { 3》 *p = 100; //但是这里修改了 int b = 20; p = b; //这一步反而会报错 } int main() { int a = 10; int* p = &a; 1》 func(p); //这里传的是int* 类型 4》 cout << a << endl; //并且在外部还可以正常打印出100 return 0; } 由上可见其实对于函数参数的类型的推演,也就是第二步我们就判断错误了, const int a = 10; int const b = 10; 对于上面两个const修饰的都是变量a,b和位置并没有关系,也就是说在参数列表中T 就仅仅只是一个变量, 别管是int* ,int,int**,反正const修饰的只是后面的变量。模板特化: 全特化:对所有类型的参数进行特化
template<class T1,class T2> class Date { public: Date() {} private: T1 _year; T2 _month; }; ///上面类的特化 template<> class Date { public: Date() {} private: int _year; int _month; }; Date<int,int> m; //调用特化版本 Date<int,double> w; // 调用模板类偏特化: 又包含部分特化:—》将模板参数中的部分类型进行特化
template<class T1, class T2> class Date { public: Date() {} private: T1 _year; T2 _month; }; ///上面类的偏特化 //意思就是只要在定义类对象的时候,给出的第二个参数是int类型的就调用偏特化版本 template<class T1, int> class Date { public: Date() {} private: T1 _year; int _month; }; Date<int, int> m; //调用偏特化 Date<int, double> w; // 调用模板类非类型模板参数必须在编译器就确认其结果,
template<class T, size_t N> class array { public: ... private: T _array[N]; //N是常量 size_t _size; }; int a = 10; int b = 20 array<int,a + b > arr;//必须在编译期给出结果,这里会编译报错偏特化的第二个特性:让米板参数列表中的类型更加的严格
//假如我们想让模板只能处理指针类型的参数,那么就可以这样做 //让模板参数列表的限制更加的严格 template<class T1, class T2> class Date<T1*,T2*> { public: Date() {} private: T1* _year; T2* _month; }; Date<int*, int*> m; //调用偏特化 Date<int*, double*> w; // 调用模板类