C++ Primer 学习笔记_54_STL剖析(九):迭代器适配器{(插入迭代器back_insert_iterator)、IO流迭代器(istream_iterator、ostream_i

回顾

适配器

1、三种类型的适配器:

(1)容器适配器:用来扩展7种基本容器,利用基本容器扩展形成了栈、队列和优先级队列

(2)迭代器适配器:(反向迭代器、插入迭代器、IO流迭代器)

(3)函数适配器:函数适配器能够将仿函数和另一个仿函数(或某个值、或某个一般函数)结合起来。

【1】针对成员函数的函数适配器

【2】针对一般函数的函数适配器

一、迭代器适配器

1、反向迭代器

2、插入迭代器

3、IO流迭代器

其中反向迭代器,利用正向迭代器实现可以参考以前《46_STL剖析(三)》。

二、插入迭代器

1、插入迭代器实际上是一个输出迭代器(*it=; ++),重载了*运算符,=运算符和++运算符

2、向后插入

back_insert_iterator

back_inserter

3、向前插入

front_insert_iterator

front_inserter

4、示例1

#include <iostream>
#include <vector>
#include <iterator>
#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);  //没用:相当于v.push_back(6);
    bii = 6;  //等同于:*bii = 6;
    ShowVec(v);

    back_insert_iterator<vector<int> > bii2(v2);  //有用:跌代器结合copy函数
    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;
}

运行结果:

5、源码分析

查看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 函数,主要是以下代码:

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 是可以使用的。

6、示例2

#include <iostream>
#include <vector>
#include <list>
#include <iterator>
#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> l2;

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

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

运行结果:

三、IO流迭代器

1、输出流迭代器(ostream_iterator)

*it=; ++

2、输入流迭代器(istream_iterator)

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

3、示例

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

using namespace std;

int main(void)
{
    //int a[] = {1, 2, 3, 4, 5};
    //vector<int> v(a, a+5);
    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;
}

运行结果:

输入:1 2 3 4 5

4、源码分析

首先来看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++编程规范

时间: 2024-08-04 14:16:05

C++ Primer 学习笔记_54_STL剖析(九):迭代器适配器{(插入迭代器back_insert_iterator)、IO流迭代器(istream_iterator、ostream_i的相关文章

C++ Primer 学习笔记_53_STL剖析(八):函数适配器:bind2nd 、mem_fun_ref 、函数适配器应用举例

回顾 五.STL中内置的函数对象 一.适配器 1.三种类型的适配器: (1)容器适配器:用来扩展7种基本容器,利用基本容器扩展形成了栈.队列和优先级队列 (2)迭代器适配器:(反向迭代器.插入迭代器.IO流迭代器) (3)函数适配器:函数适配器能够将仿函数和另一个仿函数(或某个值.或某个一般函数)结合起来. [1]针对成员函数的函数适配器 [2]针对一般函数的函数适配器 二.函数适配器 1.示例 #include <iostream> #include <algorithm> #i

C++ Primer 学习笔记_46_STL剖析(一):泛型程序设计、什么是STL、STL六大组件及其关系

一.泛型程序设计 1.泛型编程(generic programming):相同的逻辑和算法,对不同类型的数据进行处理 2.将程序写得尽可能通用 3.将算法从数据结构中抽象出来,成为通用的 4.C++的模板为泛型程序设计奠定了关键的基础 二.什么是STL 1.STL(Standard Template Library),即标准模板库,是一个高效的C++程序库. 2.包含了诸多在计算机科学领域里常用的基本数据结构和基本算法.为广大C++程序员们提供了一个可扩展的应用框架,高度体现了软件的可复用性 3

C++ Primer 学习笔记_55_STL剖析(十):容器适配器(stack、 queue 、priority_queue)源码浅析与使用示例

七种基本容器:vector.deque.list.set.multiset.map.multimap 一.容器适配器 stack queue priority_queue stack.queue.priority_queue 都不支持任一种迭代器,它们都是容器适配器类型,stack是用vector/deque/list对象创建了一个先进后出容器:queue是用deque或list对象创建了一个先进先出容器:priority_queue是用vector/deque创建了一个排序队列,内部用二叉堆实

C++ Primer 学习笔记_56_STL剖析(十一)(原boost库):详解智能指针(unique_ptr(原scoped_ptr) 、shared_ptr 、weak_ptr源码分析)

注意:现在boot库已经归入STL库,用法基本上还和boost类似 在C++11中,引入了智能指针.主要有:unique_ptr, shared_ptr, weak_ptr. 这3种指针组件就是采用了boost里的智能指针方案.很多有用过boost智能指针的朋友,很容易地就能发现它们之间的关间: std boost 功能说明 unique_ptr scoped_ptr 独占指针对象,并保证指针所指对象生命周期与其一致 shared_ptr shared_ptr 可共享指针对象,可以赋值给shar

C++ Primer 学习笔记_43_STL实践与分析(17)--再谈迭代器【中】

STL实践与分析 --再谈迭代器[中] 二.iostream迭代[续] 3.ostream_iterator对象和ostream_iterator对象的使用 能够使用ostream_iterator对象将一个值序列写入流中,其操作过程与使用迭代器将一组值逐个赋值给容器中的元素同样: ostream_iterator<string> out_iter(cout,"\n"); istream_iterator<string> in_iter(cin),eof; wh

C++ Primer 学习笔记_98_特殊工具与技术 --优化内存分配

特殊工具与技术 --优化内存分配 引言: C++的内存分配是一种类型化操作:new为特定类型分配内存,并在新分配的内存中构造该类型的一个对象.new表达式自动运行合适的构造函数来初始化每个动态分配的类类型对象. new基于每个对象分配内存的事实可能会对某些类强加不可接受的运行时开销,这样的类可能需要使用用户级的类类型对象分配能够更快一些.这样的类使用的通用策略是,预先分配用于创建新对象的内存,需要时在预先分配的内存中构造每个新对象. 另外一些类希望按最小尺寸为自己的数据成员分配需要的内存.例如,

C++ Primer 学习笔记_73_面向对象编程 --再谈文本查询示例

面向对象编程 --再谈文本查询示例 引言: 扩展第10.6节的文本查询应用程序,使我们的系统可以支持更复杂的查询. 为了说明问题,将用下面的简单小说来运行查询: Alice Emma has long flowing red hair. Her Daddy says when the wind blows through her hair, it looks almost alive, like a fiery bird in flight. A beautiful fiery bird, he

C++ Primer 学习笔记_99_特殊工具与技术 --优化内存分配[续1]

特殊工具与技术 --优化内存分配[续1] 三.operator new函数和operator delete 函数 – 分配但不初始化内存 首先,需要对new和delete表达式怎样工作有更多的理解.当使用new表达式 string *sp = new string("initialized"); 的时候,实际上发生三个步骤: 1)首先,表达式调用名为operator new 的标准库函数,分配足够大的原始的未类型化的内存,以保存指定类型的一个对象; 2)接下来,运行该类型的一个构造函数

C++ Primer 学习笔记_81_模板与泛型编程 --类模板成员[续1]

模板与泛型编程 --类模板成员[续1] 二.非类型形参的模板实参 template <int hi,int wid> class Screen { public: Screen():screen(hi * wid,'#'), cursor(hi * wid),height(hi),width(wid) {} //.. private: std::string screen; std::string::size_type cursor; std::string::size_type height