STL源码剖析学习十九:配接器
配接器:一种设计模式
将一个class的接口转换成另一个class的接口,使原来因为接口不兼容而不能合作的class可以一起运作
容器配接器
queue 和 stack 修士deque的接口成了另一种容器
迭代器配接器
Insert Iterator:
将一般的赋值操作变成插入操作,包括back_inserter_iterator,front_insert_iterator,insert_iterator
STL提供三个相应的函数back_inserter(container& x), front_inserter(container& x),inserter(container& x, iterator i)
每个插入迭代器内部维护一个容器(由用户指定),当对插入迭代器做赋值操作时就将其转为对容器的迭代器做插入操作
就是说再调用operator=时改成调用底部容器的push_front或者push_back或者insert操作
而对于--、++、*、->操作都被屏蔽,因为都是没有意义的
Reverse Iterator:
将一般迭代器的行进方向逆转
rbegin(){ return reverse_iterator(end()); } rend(){ return reverse_iterator(end()); }
为了配合迭代器前闭后开的原则,迭代器被逆转方向时,虽然其实际位置(真实的地址)没有变,但是其逻辑位置(指向的元素)变了。
这样当一个正向迭代器转为你想迭代器之后,不需要任何附加处理就可以应用算法,以相反的次序处理区间
iterator_type base() const { return current; }//取出相应的正向迭代器 reference operator*() const { Iterator tmp = current; return *--tmp; //对逆向迭代器取值就是将正向迭代器推一格之后再取值 } //前进后退逆置 self& operator++() { --current; return *this; }
Iostream iterator:
可以将迭代器绑定到一个iostream对象身上
所谓绑定一个输入流对象,其实就是在流迭代器内部维护一个输入流成员,客户端对这个迭代器做++操作时,会被引导调用迭代器内部所含的那个流对象做输入操作>>。
这个迭代器是一个输入迭代器,不具备--能力
class istream_iterator { void read() { end_marker = (*stream) ? true : false; if(end_marker) //关键就是输入 *stream >> value; //不断判断流的状态以决定是否要继续输入 end_marker = (*stream) ? true : false; } istream_iterator() : stream(&cin), end_marker(false){} istream_iterator(istream& s) : stream(&s) { read(); } //用法: //istream_iterator<int> eos; 引发end_marker为false //istream_iterator<int> initer(cin); 立即引发read()等待输入 //永远在最必要的时候才定义一个istream_iterator istream_iterator<T, Distance>& operator++() { read(); return *this; } istream_iterator<T, Distance> operator++(int)() { istream_iterator<T, Distance> tmp = *this; read(); return tmp; } }
ostream类似于istream
在内部维护一个输出流对象,客户端对这个迭代器所做的operator=操作,会被引导调用对应的输出流对象的输出操作<<
ostream_iterator<T>& operator=(const T& value) { *stream << value;//输出数值 if(string) *steam << string;//如果间隔符号不为空,输出间隔符号 return *this; }
迭代器配接器很少以迭代器为直接参数,通常以容器为直接参数,每个容器都有自己的专属迭代器,因此配接器其实是以迭代器为间接参数
所谓对迭代器的修饰,只是一种观念上的转变
给个综合应用的例子
#include<iterator> #include<deque> #include<algorithm> #include<iostream> using namespace std; int main() { ostream_iterator<int> outite(cout, " "); //将outite绑定到cout int ia[] = { 0,1,2,3,4,5 }; deque<int> id(ia, ia+6); copy(id.begin(), id.end(), outite);//输出 cout<<endl; copy(ia+1, ia+2, front_inserter(id));//将拷贝变成从头部插入 copy(id.begin(), id.end(), outite); cout<<endl; copy(ia+3, ia+4, back_inserter(id));//从尾部插入 copy(id.begin(), id.end(), outite); cout<<endl; deque<int>::iterator ite = find(id.begin(), id.end(), 5);//查找到5所在的位置 copy(id.begin(), id.end(), inserter(id, ite));//从该位置开始插入 copy(id.begin(), id.end(), outite); cout<<endl; copy(id.rbegin(), id.rend(), outite);//反向迭代器,反向输出容器中的内容 cout<<endl; istream_iterator<int> inite(cin), eos;//将迭代器和输入流绑定,eos为终止符 copy(inite, eos, inserter(id, id.begin()));//从标准输入读入内容并且从头部插入到容器中 copy(id.begin(), id.end(), outite); cout<<endl; }
functor adapter:
通过他们之间的绑定,组合,修饰能力,集合可以无限创造出各种表达式,搭配算法一起运用
每一个函数对象配接器内部也藏了一个成员对象,其型号等于它所要配接的对象。
当有一份被修饰对象的副本之后,就成了该修饰对象的主人,可以有资格调用该修饰对象并且在返回值和参数上所修改
辅助函数 实际效果 bind1st(op, x) op(x, param) bind2nd(op, x) op(param, x) not1(pred) !pred(param) not2(pred) !pred(param1, param2) ptr_fun(result(*fp)(Arg)) fp(param); ptr_fun(result(*fp)(Arg1, Arg2)) fp(param1, param2) mem_fun(S (T::*f)()) (param->*f)() men_fun_ref(S (T::*f)()) (param.*f)()
#include<algorithm> #include<functional> #include<vector> #include<iostream> #include<iterator> using namespace std; int main() { ostream_iterator<int> outite(cout, " "); int ia[] = { 0,1,2,3,4,5 }; vector<int> iv(ia, ia+6); //compose1不在规范中 for_each(iv.begin(), iv.end(), compose1(bind2nd(multiplies<int>(),3),bind2nd(plus<int>(),2) )); copy(iv.begin(), iv.end(), outite); cout<<endl; transform(iv.begin(), iv.end(), outite, compose1(bind2nd(multiplies<int>(),3),bind2nd(plus<int>(),2) )); }
所有期望获得配接能力的组件,本身都必须是可配接的
一元函数必须继承自unary_function 二元函数必须继承自binary_function
为了将一般函数和成员函数都纳入STL中,可以被配接器调用,提供mem_fun()和ptr_fun()函数
#include<algorithm> #include<functional> #include<vector> #include<iostream> #include<iterator> using namespace std; void print(int i) { cout<<i<<" "; } class Int { public: explicit Int(int i):m_i(i){} void print1()const { cout<<"["<<m_i<<"]"; } private: int m_i; }; int main() { ostream_iterator<int> outite(cout, " "); int ia[] = { 2, 21, 13, 7, 9, 23 }; vector<int> iv(ia, ia+6); cout<<count_if(iv.begin(), iv.end(), not1(bind2nd(less<int>(), 12))); cout<<endl; //用函数指针搭配STL算法 for_each(iv.begin(), iv.end(), print); cout<<endl; //用修饰过的一般函数搭配STL算法 for_each(iv.begin(), iv.end(), ptr_fun(print)); cout<<endl; Int t1(3), t2(7), t3(20), t4(14), t5(68); vector<Int> Iv; Iv.push_back(t1); Iv.push_back(t2); Iv.push_back(t3); Iv.push_back(t4); Iv.push_back(t5); //用修饰过的成员函数搭配STL算法 //当容器中存放的是对象的时候用mem_fun_ref当存指向对象的指针时用mem_fun for_each(Iv.begin(), Iv.end(), mem_fun_ref(&Int::print1)); cout<<endl; }
泛型和多态之间的搭配
#include<iostream> #include<vector> #include<algorithm> #include<functional> using namespace std; class shape { public: virtual void display() = 0; }; class rect : public shape { public: virtual void display(){ cout<<"rect"<<endl; } }; class circle : public shape { public: virtual void display(){ cout<<"circle"<<endl; } }; class square : public rect { public: virtual void display(){ cout<<"square"<<endl; } }; int main() { vector<shape*> v; //容器中存放的是指针 所以下面可以直接用mem_fun //STL容器不支持引用语意 v.push_back(new rect); v.push_back(new circle); v.push_back(new square); v.push_back(new circle); v.push_back(new rect); for(int i=0; i<v.size(); ++i) (v[i])->display(); cout<<endl; for_each(v.begin(), v.end(), mem_fun(&shape::display)); cout<<endl; }