从零开始学C++之STL(十):迭代器适配器{(插入迭代器back_insert_iterator)、IO流迭代器(istream_iterator、ostream_iterator)}

一、迭代器适配器

反向迭代器 插入迭代器 IO流迭代器

其中反向迭代器可以参考以前的文章

二、插入迭代器

插入迭代器实际上是一个输出迭代器(*it=; ++) back_insert_iterator back_inserter front_insert_iterator front_inserter

先来看示例:

#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

void ShowVec(const vector<int> &v)
{
    for (vector<int>::const_iterator it = v.begin(); it != v.end(); ++it)
    {
        cout << *it << ' ';
    }
    cout << endl;
}
int main(void)
{
    int a[] = {1, 2, 3, 4, 5};
    vector<int> v(a, a + 5);
    vector<int> v2;

    back_insert_iterator<vector<int> > bii(v);
    //*bii = 6;
    bii = 6;
    ShowVec(v);

    back_insert_iterator<vector<int> > bii2(v2);
    copy(v.begin(), v.end(), bii2);
    ShowVec(v2);

    back_inserter(v) = 7;
    ShowVec(v);

    copy(v.begin(), v.end(), back_inserter(v2));
    ShowVec(v2);


    return 0;
}

查看back_insert_iterator 类的定义:

// TEMPLATE CLASS back_insert_iterator
template<class _Container>
class back_insert_iterator
    : public _Outit
{
    // wrap pushes to back of container as output iterator
public:
    typedef _Container container_type;
    typedef typename _Container::reference reference;

    typedef _Range_checked_iterator_tag _Checked_iterator_category;

    explicit back_insert_iterator(_Container &_Cont)
        : container(&_Cont)
    {
        // construct with container
    }

    back_insert_iterator<_Container> &operator=(
        typename _Container::const_reference _Val)
    {
        // push value into container
        container->push_back(_Val);
        return (*this);
    }

    back_insert_iterator<_Container> &operator*()
    {
        // pretend to return designated value
        return (*this);
    }

    back_insert_iterator<_Container> &operator++()
    {
        // pretend to preincrement
        return (*this);
    }

    back_insert_iterator<_Container> operator++(int)
    {
        // pretend to postincrement
        return (*this);
    }

protected:
    _Container *container;  // pointer to container
};

类内部的成员container 保存的是指向容器的指针,重载了*, ++, = 等运算符,* 和 ++ 返回的都是迭代器本身,主要看 赋值运算符:

container->push_back(_Val); 即调用了容器的push_back 函数, 所以可以直接写 bii = 6; 即将6压入容器末尾。程序中还调用了copy 

函数,回顾copy 源码,主要是以下代码:

for (; _First != _Last; ++_Dest, ++_First)

        *_Dest = *_First;

其中,_First 和 _Last 分别是v.begin() 和 v.end(), _Dest 是 bii2,上面也说了,*_Dest 返回的是自身,而且++_Dest 返回的也是自

身,从_First 遍历到 _Last ,调用back_insert_iterator 类的operator=,即不断地执行container->push_back(_Val); 容器的元素位置会

自动移动。

再来看back_inserter 函数:

// TEMPLATE FUNCTION back_inserter
template<class _Container> inline
back_insert_iterator<_Container> back_inserter(_Container &_Cont)
{
    // return a back_insert_iterator
    return (std::back_insert_iterator<_Container>(_Cont));
}

实际上返回的也是一个back_insert_iterator 对象,所以能直接替换掉bii2。

当然了,与back 配对的就是front,back 是末尾插入,front 是头端插入,需要注意的是front_insert_iterator 的operator= 调用了

push_front 函数,故如vector 是没有实现push_front 的,不能使用front_insert_iterator ,而list 和 deque 是可以使用的。

示例代码如下:

#include <iostream>
#include <vector>
#include <list>
#include <algorithm>

using namespace std;

void ShowList(const list<int> &v)
{
    for (list<int>::const_iterator it = v.begin(); it != v.end(); ++it)
    {
        cout << *it << ' ';
    }
    cout << endl;
}

int main(void)
{
    int a[] = {1, 2, 3, 4, 5};
    list<int> l(a, a + 5);
    list<int> ll;

    front_insert_iterator<list<int> > fii(l);
    fii = 0;
    ShowList(l);

    copy(l.begin(), l.end(), front_inserter(ll));
    ShowList(ll);
    return 0;
}

三、IO流迭代器

输出流迭代器(ostream_iterator)

*it=; ++

输入流迭代器(istream_iterator)

=*it; ->; ++; ==; !=

直接来看示例代码:

#include <iostream>
#include <vector>
#include <list>
#include <algorithm>

using namespace std;

int main(void)
{
    vector<int> v;

    // copy from cin to vector
    copy(istream_iterator<int>(cin), istream_iterator<int>(), back_inserter(v));

    // copy from vector to cout
    copy(v.begin(), v.end(), ostream_iterator<int>(cout, " "));
    cout << endl;

    return 0;
}

首先来看istream_iterator 的源码:

// TEMPLATE CLASS istream_iterator
template < class _Ty,
         class _Elem = char,
         class _Traits = char_traits<_Elem>,
         class _Diff = ptrdiff_t >
class istream_iterator
    : public iterator < input_iterator_tag, _Ty, _Diff,
      const _Ty *, const _Ty & >
{
    // wrap _Ty extracts from input stream as input iterator
    typedef istream_iterator<_Ty, _Elem, _Traits, _Diff> _Myt;
public:
    typedef _Elem char_type;
    typedef _Traits traits_type;
    typedef basic_istream<_Elem, _Traits> istream_type;

#if _SECURE_SCL
    typedef _Range_checked_iterator_tag _Checked_iterator_category;
#endif

    istream_iterator()
        : _Myistr(0)
    {
        // construct singular iterator
    }

    istream_iterator(istream_type &_Istr)
        : _Myistr(&_Istr)
    {
        // construct with input stream
        _Getval();
    }

    const _Ty &operator*() const
    {
        // return designated value

        return (_Myval);
    }

    const _Ty *operator->() const
    {
        // return pointer to class object
        return (& **this);
    }

    _Myt &operator++()
    {
        // preincrement

        _Getval();
        return (*this);
    }


protected:
    void _Getval()
    {
        // get a _Ty value if possible
        if (_Myistr != 0 && !(*_Myistr >> _Myval))
            _Myistr = 0;
    }

    istream_type *_Myistr;  // pointer to input stream
    _Ty _Myval; // lookahead value (valid if _Myistr is not null)
};

上面只截取了部分用上的源码,istream_iterator 类有两个成员,一个是输入流对象指针,一个是输入的值,如

istream_iterator<int>(cin)  调用构造函数,初始化_Myistr,且通过函数_Getval() 初始化_Myval,_Getval() 调用输入流的

operator>> 将键盘输入的值赋予_Myval。而 istream_iterator<int>() 呢初始化_Myistr 为0,此时_Myval 被忽略。

回顾copy 源码,主要是以下代码:

for (; _First != _Last; ++_Dest, ++_First)

        *_Dest = *_First;

此时_First 和 _Last 是 istream_iterator<int> 类型,_Dest是back_insert_iterator 类型,而判断_First 和 _Last 是否相等,其实

operator != 里面是判断它们的成员指针_Myistr 是否相等,在_Getval 函数可以看到,当我们输入错误(类型不匹配)或者ctrl+z,

则 istream_iterator<int>(cin) 的_Myistr 被置为0,此时本来 istream_iterator<int>() 的_Myistr 就为0,故相等,不再继续执行下去。

如果不等,即输入正确的话,*First 调用istream_iterator 类的operator* 直接返回_Myval ,接着调用back_insert_iterator 类的

operator=,即调用container 的push_back ,将_Myval 压入容器。++_Dest 是没什么效果的,而++_First 在istream_iterator 类的

operator++ 中会继续调用_Getval,即继续获得键盘输入覆盖_Myval。

再来看ostream_iterator 的源码:

// TEMPLATE CLASS ostream_iterator
template<class _Ty,
    class _Elem = char,
    class _Traits = char_traits<_Elem> >
    class ostream_iterator
        : public _Outit
    {   // wrap _Ty inserts to output stream as output iterator
public:
    typedef _Elem char_type;
    typedef _Traits traits_type;
    typedef basic_ostream<_Elem, _Traits> ostream_type;

#if _SECURE_SCL
    typedef _Range_checked_iterator_tag _Checked_iterator_category;
#endif

    ostream_iterator(ostream_type& _Ostr,
        const _Elem *_Delim = 0)
        : _Myostr(&_Ostr), _Mydelim(_Delim)
        {   // construct from output stream and delimiter
        }

    ostream_iterator<_Ty, _Elem, _Traits>& operator=(const _Ty& _Val)
        {   // insert value into output stream, followed by delimiter
        *_Myostr << _Val;
        if (_Mydelim != 0)
            *_Myostr << _Mydelim;

        return (*this);
        }

    ostream_iterator<_Ty, _Elem, _Traits>& operator*()
        {   // pretend to return designated value
        return (*this);
        }

    ostream_iterator<_Ty, _Elem, _Traits>& operator++()
        {   // pretend to preincrement
        return (*this);
        }

protected:

    const _Elem *_Mydelim;  // pointer to delimiter string (NB: not freed)
    ostream_type *_Myostr;  // pointer to output stream
    };

ostream_iterator 类也有两个成员,一个是输出流对象指针,一个是字符串指针,看上面的copy 代码,此时_First 和 _Last 

分别是v.begin() 和 v.end(),_Dest是 ostream_iterator<int> 类型,*_Dest 返回自身,++_Dest 也返回自身,而在operator= 函数中

*_Myostr << _Val;

if (_Mydelim != 0)

*_Myostr << _Mydelim;

即判断如果还有传入字符串,则在输出元素值之后,还伴随着字符串的输出。所以示例代码中的输出是伴随着空格的。

参考:

C++ primer 第四版 Effective C++ 3rd C++编程规范

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏PPV课数据科学社区

【工具】Python正则表达式的七个使用范例

? 作为一个概念而言,正则表达式对于Python来说并不是独有的。但是,Python中的正则表达式在实际使用过程中还是有一些细小的差别。 本文是一系列关于Py...

3299
来自专栏我是攻城师

Apache Pig学习笔记之内置函数(三)

3614
来自专栏张善友的专栏

Enumerable#zip特性

看到文章Zip operator in Linq with .NET 4.0, Enumerable#zip是Ruby 1.8开始出现的特性,.NET 4/Si...

1869
来自专栏九彩拼盘的叨叨叨

Sass 写法示例

CSS 本身是非常强大的,但随着样式表变大,变复杂,维护 CSS 变得越来越难。这时候预处理就有用了。Sass 是一种预处理,它能让你使用一些 CSS 中没有的...

761
来自专栏青玉伏案

数据结构回顾之顺序存储结构中的线性表(栈与队列顺序线性表实现)

说到数据结构呢,对于一个Coder来说还是蛮重要的啦,每次看数据结构的东西都有新的收获,这两天在回顾数据结构的知识。当然啦,虽然数据结构有些是理论的东西,如...

1807
来自专栏杨建荣的学习笔记

任务调度并行算法的Java简单实现

今天下午抽空写了下并行调度算法的Java版本,是想把这个思路先实现了,后面改写Python版作为参考,调试这个版本之后,再来写Python版,发现差别还不小。 ...

3156
来自专栏about云

日志分析实战之清洗日志小实例6:获取uri点击量排序并得到最高的url

问题导读 1.读取日志的过程中,发生异常本文是如何解决的? 2.读取后,如何过滤异常的记录? 3.如何实现统计点击最高的记录? 日志分析实战之清洗日志...

2973
来自专栏无所事事者爱嘲笑

js小题目(持续更新)

1504
来自专栏高性能服务器开发

(四)sds字符串

今天分析的是Redis源码中的字符串操作类的代码实现。有了上几次的分析经验,渐渐觉得我得换一种分析的方法,如果每个API都进行代码分析,有些功能性的重复,导致分...

33810
来自专栏光变

Java编程风格

Java编程的风格介绍,主要参考乐google的java code style。对模糊部分作出了明确的选择。

812

扫码关注云+社区