前言
配接器(adapters)在 STL 组件的灵活组合运用功能上,扮演着轴承、转换器的角色。Adapter 这个概念,事实上是一种设计模式。 《Design Patterns》一书中对 adapter 样式的定义如下:将一个类的接口转换为另一个类的接口,使原本因接口不兼容而不能合作的类,可以一起运作。
STL 中所提供的配接器,按照配接的对象可以分为三类:
改变 Container 接口的配接器:Containers Adapter
改变 Iterator 接口的配接器:Iterators Adapter
改变 Functor 接口的配接器:Functors Adapter
其中,container Adapter 已经在前面介绍 stack & queue 中介绍过,这两个容器都是通过修饰 deque 接口的方式呈现出另一种容器的样子,这里就不再介绍了。本节将重点介绍 Iterator Adapter 的内容。
Iterator Adapter
STL 提供了许多应用于迭代器身上的配接器,包括 insert iterators、reverse iterators、iostream iterators 等,这些配接器都通过修饰原始的 iterators 对外接口的方式来实现不同的行为。
Insert Iterators
Insert iterators 可以将一般迭代器的赋值(assign)操作转变为插入(insert)操作 。这样的迭代器包括专司尾端插人操作的 back_insert_iterator
,专司头端插人操作的 front_insert_iterator
,以及可从任意位置执行插入操作的 inser_iterator
。由于这三个 iterator adapters 的使用接口不是十分直观,给一般用户带来困扰,
因此,STL更提供三个相应函数: back_inserter()
、front_inserter()
、inserter()
,直接返回一个容器的插入迭代器。
Insert iterators 的主要观念是,每一个 insert iterators 内部都维护有一个容器(必须由用户指定);容器当然有自己的迭代器,于是,当客户端对 insert iterators 做赋值操作时,就在 insert iterators 中被转为对该容器的迭代器做插入操作。
也就是说,在 insert iterators 的 operator=
操作符中调用底层容器的 push_front()
或 push_back()
或 insert()
操作函数。至于其它的迭代器惯常行为如 operator++
,operator++(int)
,operator*
都被关闭功能,更没有提供 operator--(int)
或 operator--
或 operator->
等功能(因此被类型被定义为 output_iterator_tag
)。
换句话说,insert iterators 的前进、后退、取值、成员取用等操作都是没有意义的,甚至是不允许的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 template <class Container >class back_insert_iterator {protected : Container* container; public : typedef output_iterator_tag iterator_category; typedef void value_type; typedef void difference_type; typedef void pointer; typedef void reference; typedef Container container_type; explicit back_insert_iterator (Container& x) :container(&x) { } back_insert_iterator& operator =(const typename Container::value_type& value) { container->push_back (value); return *this ; } back_insert_iterator& operator =(typename Container::value_type&& value) { container->push_back (move (value)); return *this ; } back_insert_iterator& operator *() { return *this ; } back_insert_iterator& operator ++() { return *this ; } back_insert_iterator& operator ++(int ) { return *this ; } }; template <class Container >inline back_insert_iterator<Container> back_inserter (Container& x) { return back_insert_iterator <Container>(x); }
back_insert_iterator
就是将迭代器的赋值操作修改为容器的 push_back()
操作,即从容器的尾端插入元素。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 template <class Container >class front_insert_iterator {protected : Container* container; public : typedef output_iterator_tag iterator_category; typedef void value_type; typedef void difference_type; typedef void pointer; typedef void reference; typedef Container container_type; explicit front_insert_iterator (Container& x) :container(&x) { } front_insert_iterator& operator =(const typename Container::value_type& value) { container->push_front (value); return *this ; } front_insert_iterator& operator =(typename Container::value_type&& value) { container->push_front (move (value)); return *this ; } front_insert_iterator& operator *() { return *this ; } front_insert_iterator& operator ++() { return *this ; } front_insert_iterator& operator ++(int ) { return *this ; } }; template <class Container >inline front_insert_iterator<Container> front_inserter (Container& x) { return front_insert_iterator <Container>(x); }
front_insert_iterator
将迭代器的赋值操作修改为容器的 push_front()
操作,即从头部插入元素。但需要注意的是,由于 vector 容器不支持 push_front
操作,因此该迭代器不适用于 vector。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 template <class Container >class insert_iterator {protected : Container* container; typename Container::iterator iter; public : typedef output_iterator_tag iterator_category; typedef void value_type; typedef void difference_type; typedef void pointer; typedef void reference; typedef Container container_type; insert_iterator (Container& x, typename Container::iterator i) :container (&x), iter (i) {} insert_iterator& operator =(const typename Container::value_type& value) { iter = container->insert (iter, value); ++iter; return *this ; } insert_iterator& operator =(typename Container::value_type&& value) { iter = container->insert (iter, move (value)); ++iter; return *this ; } insert_iterator& operator *() { return *this ; } insert_iterator& operator ++() { return *this ; } insert_iterator& operator ++(int ) { return *this ; } }; template <class Container , class Iterator >inline insert_iterator<Container> inserter (Container& x, Iterator i) { typedef typename Container::iterator iter; return insert_iterator <Container>(x, iter (i)); }
insert_iterator
将迭代器的赋值操作修改为容器的 insert
操作,并且将迭代器右移一个位置 ,以保证插入操作的连续性。这是与 back_insert_iterator
及 front_insert_iterator
不同的地方。
reverse_iterator
Reverse iterator,就是将迭代器的移动行为倒转。如果 STL 算法接受的不是一般正常的迭代器,而是这种逆向迭代器,它就会以从尾到头的方向来处理序列中的元素。
实际上我们对于这种配接器并不陌生,因为随便打开之前实现过的一个容器,都会发现类似于下面这样的定义:
1 2 reverse_iterator rbegin () noexcept { return reverse_iterator (end ()); }reverse_iterator rend () noexcept { return reverse_iterator (begin ()); }
没错,取得 rend()
与 rbegin()
等迭代器,就是靠 reverse_iterator
进行修饰。虽然看似只需要翻转迭代器所有的移动行为就行,但实际上 reverse_iterator
还是暗藏玄机的,主要体现在对于 operator*
的重载上:
1 2 3 4 5 reference operator *() const { auto tmp = current; return *--tmp; }
由于迭代器的区间表示是一种“前闭后开”的方式,换言之是不对称的;因此,为了配合这样的不对称,当迭代器改变方向时,虽然其真实位置(真正的地址)不变,但其逻辑位置(迭代器所代表的元素,即 operator*
取出的元素)却改变了。因为这样才能保证:将一个正向迭代器区间转换为一个逆向迭代器区间后,不必再有任何的额外处理 。
下图可以帮助我们理解这一逻辑:
reverse_iterator
的代码实现如下。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 template <class Iterator >class reverse_iterator { private : Iterator current; public : typedef typename iterator_traits<Iterator>::iterator_category iterator_category; typedef typename iterator_traits<Iterator>::value_type value_type; typedef typename iterator_traits<Iterator>::difference_type difference_type; typedef typename iterator_traits<Iterator>::pointer pointer; typedef typename iterator_traits<Iterator>::reference reference; typedef Iterator iterator_type; typedef reverse_iterator<Iterator> self; public : reverse_iterator () {} explicit reverse_iterator (iterator_type i) :current(i) { } reverse_iterator (const self& rhs) :current (rhs.current) {} public : iterator_type base () const { return current; } reference operator *() const { auto tmp = current; return *--tmp; } pointer operator ->() const { return &(operator *()); } self& operator ++() { --current; return *this ; } self operator ++(int ) { self tmp = *this ; --current; return tmp; } self& operator --() { ++current; return *this ; } self operator --(int ) { self tmp = *this ; ++current; return tmp; } self& operator +=(difference_type n) { current -= n; return *this ; } self operator +(difference_type n) const { return self (current - n); } self& operator -=(difference_type n) { current += n; return *this ; } self operator -(difference_type n) const { return self (current + n); } reference operator [](difference_type n) const { return *(*this + n); } };
stream iterator
stream iterctors,可以将迭代器绑定到一个 stream(数据流)对象身上。绑定到 istream对象(例如 std::cin
)者,称为istream_iterator,拥有输入能力;绑定到ostream对象(例如 std::cout
)者,称为 ostream_iterator,拥有输出能力。
istream iterator
所谓绑定一个 istream object,其实就是在 istrearmn iterator 内部维护一个 istream member,客户端对于这个迭代器所做的 operator++
操作、会被导引调用迭代器内部所含的那个 istream member 的输入操作(operator>>
)。这个迭代器是个 Input lterartor,不具备 operator--
。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 template <class T , class Distance = ptrdiff_t >class istream_iterator {protected : std::istream* stream; T value; bool end_marker; public : typedef input_iterator_tag iterator_category; typedef T value_type; typedef Distance difference_type; typedef const T* pointer; typedef const T& reference; istream_iterator () :stream (&std::cin), end_marker (false ) {} istream_iterator (std::istream& s) :stream (&s) { read (); } reference operator *() const { return value; } pointer operator ->() const { return &(operator *()); } istream_iterator& operator ++() { read (); return *this ; } istream_iterator operator ++(int ) { istream_iterator tmp = *this ; read (); return tmp; } void read () { end_marker = (*stream) ? true : false ; if (end_marker) *stream >> value; end_marker = (*stream) ? true : false ; } bool equal (const istream_iterator& rhs) const { return (end_marker == rhs.end_marker) && (!end_marker || (stream == rhs.stream)); } }; template <class T , class Distance = ptrdiff_t >inline bool operator ==(const istream_iterator<T, Distance>& lhs, const istream_iterator<T, Distance>& rhs) { return lhs.equal (rhs); } template <class T , class Distance = ptrdiff_t >inline bool operator !=(const istream_iterator<T, Distance>& lhs, const istream_iterator<T, Distance>& rhs) { return !(lhs == rhs); }
ostream iterator
至于 ostream iterator,所谓绑定一个 ostream object,就是在其内部维护一个 ostream member,客户端对于这个迭代器所做的 operator=
操作,会被导引调用对应的(迭代器内部所含的)那个 ostream member 的输出操作(operator<<
)。这个迭代器是个OutputIterator。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 template <class T , class Distance = ptrdiff_t >class ostream_iterator {protected : std::ostream* stream; const char * string; public : typedef output_iterator_tag iterator_category; typedef void value_type; typedef void difference_type; typedef void pointer; typedef void reference; ostream_iterator (std::ostream& s) :stream (&s), string (nullptr ) {} ostream_iterator (std::ostream& s, const char * c) :stream (&s), string (c) {} ostream_iterator& operator =(const T& value) { *stream << value; if (string) *stream << string; return *this ; } ostream_iterator& operator *() { return *this ; } ostream_iterator& operator ++() { return *this ; } ostream_iterator& operator ++(int ) { return *this ; } };
测试
以下是一些例子,测试一下上述的三种配接器的效果。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 void test1 () { tinystl::vector<int > v1 = tinystl::vector <int >(10 ); tinystl::iota (v1.begin (), v1.end (), 0 ); tinystl::vector<int > v2; tinystl::copy (v1.begin (), v1.end (), tinystl::back_inserter (v2)); for (auto i : v2) std::cout << i << " " ; std::cout << std::endl; tinystl::copy (v2.begin (), v2.end (), tinystl::ostream_iterator <int >(std::cout, " " )); std::cout << std::endl; } void test2 () { tinystl::deque<int > d1 = tinystl::deque <int >(10 ); tinystl::iota (d1.begin (), d1.end (), 0 ); tinystl::deque<int > d2; tinystl::copy (d1.begin (), d1.end (), tinystl::front_inserter (d2)); for (auto i : d2) std::cout << i << " " ; std::cout << std::endl; tinystl::copy (d2.begin (), d2.end (), tinystl::ostream_iterator <int >(std::cout, " " )); std::cout << std::endl; } void reverse_iterator_test () { tinystl::vector<int > v1 = tinystl::vector <int >(10 ); tinystl::iota (v1.begin (), v1.end (), 0 ); auto it3 = v1.begin () + 3 ; auto reverse_it3 = tinystl::reverse_iterator<tinystl::vector<int >::iterator>(it3); std::cout << *it3 << " " << *reverse_it3 << std::endl; tinystl::for_each(v1.begin (), v1.end (), [](int i) { std::cout << i << " " ; }); std::cout << std::endl; tinystl::for_each(v1.rbegin (), v1.rend (), [](int i) { std::cout << i << " " ; }); std::cout << std::endl; } void istream_iterator_test () { tinystl::istream_iterator<int > in (std::cin) ; tinystl::istream_iterator<int > eof; tinystl::vector<int > v; tinystl::copy (in, eof, tinystl::back_inserter (v)); tinystl::copy (v.begin (), v.end (), tinystl::ostream_iterator <int >(std::cout, " " )); std::cout << std::endl; }
总结
本节简要介绍了关于迭代器的一些配接器,包括 insert_adapter
、reverse_iterator
、stream_iterator
等,作为配接器,它们都通过简单地修改 iterator 中对应接口的形式,使得迭代器呈现出不同的样貌,这正是 “配接器” 这种设计模式的灵魂所在。