错误使用std::copy通过输入流迭代器往vector对象写数据的相关源码分析

环境:gcc version 11.4.0 (Ubuntu 11.4.0-1ubuntu1~22.04) 

测试代码:

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

using namespace std;

int main(int argc, char* argv[]) {
    vector<int> vec;
    vec.reserve(10);  //预留存储空间(仅分配内存,size()仍为0) 不然调用copy 操作begin()会触发段错误
    istream_iterator<int> isi(cin);
    std::copy(isi, istream_iterator<int>(), vec.begin());//错误操作 (越界写入!)
    cout << endl;
    cout << "--------------" << endl;
    cout << &(*vec.begin()) << " " << &(*vec.end()) << endl;
    cout << "-----------------" << endl;
    copy(vec.begin(), vec.end(), ostream_iterator<int>(cout, " "));
    cout << endl;
    cout << "------------------" << endl;
    for (int i = 0; i < 10; ++i) {
        cout << vec[i] << " ";
    }
    cout << endl;
    return 0;
}

运行结果:

结果:vec 的指向起始和末尾的迭代器没变 都指向vec起始地址(代表为空)却插入了数据 遍历的时候也不能使用迭代器遍历 但确实插入了数据

看std::copy的源码:

//usr/include/c++/bits/stl_algobase.h  
//模板声明输入输出迭代器
template<typename _II, typename _OI>
    inline _OI
    copy(_II __first, _II __last, _OI __result)
    {
    //__is_move_iterator判断是否是移动迭代器
    //__miter_base移动迭代器转为普通迭代器
      return std::__copy_move_a<__is_move_iterator<_II>::__value>
         (std::__miter_base(__first), std::__miter_base(__last), __result);
    }   
//....调用链.....

//最终底层实现                                                             
  template<bool _IsMove, bool _IsSimple, typename _Category>
    struct __copy_move
    {
      template<typename _II, typename _OI>
    static _OI
    __copy_m(_II __first, _II __last, _OI __result)
    {									//(void)是显示忽略表达式结果的技巧
      for (; __first != __last; ++__result, (void)++__first)
        *__result = *__first;
      return __result;
    }
    };

std::copy 并没有边界检查 

在输入的时候 通过传入的起始迭代器进行了输入数据 ,本质上是 vec.reserve(10) 分配了至少可容纳10个 int 的​​原始内存​​ std::copy 通过 vec.begin() 的裸指针向这些内存写入数据

但是输入完成后vector对象并没有更新迭代器

正确的输入做法是使用迭代器适配器 如back_insert_iterator,底层会调用容器的push_back。

关于 std::copy(isi, istream_iterator<int>(), vec.begin())这一部分 为什么可以传入默认构造的std::istream_iterator对象表示终止 可以看输入流迭代器的源码:

//bits/stream_iterator.h
  class istream_iterator
  : public iterator<input_iterator_tag, _Tp, _Dist, const _Tp*, const _Tp&>
  {
  private:
    istream_type* _M_stream;
    _Tp       _M_value;
    bool      _M_ok;

//构造函数
  public:
    ///  Construct end of input stream iterator.
    _GLIBCXX_CONSTEXPR istream_iterator()
    : _M_stream(0), _M_value(), _M_ok(false) {}

    ///  Construct start of input stream iterator.
    istream_iterator(istream_type& __s)
    : _M_stream(std::__addressof(__s)), _M_ok(true)
    { _M_read(); }

    istream_iterator(const istream_iterator& __obj)
    : _M_stream(__obj._M_stream), _M_value(__obj._M_value),
      _M_ok(__obj._M_ok)
    { }
//_M_read()
   void
  _M_read()
  { 
    //输入流的状态为goodbit,则_M_ok = true
    //检查流指针是否有效 流读取是否成功
       					//解引用优先级大于>>
    if (_M_stream && !(*_M_stream >> _M_value))//尝试读取
      {
        //读取失败处理
        _M_stream = 0;
        _M_ok = false;
      }
  }

//不等比较操作符   
  /// Return true if the iterators refer to different streams,
  /// or if one is at end-of-stream and the other is not.
  friend bool
  operator!=(const istream_iterator& __x, const istream_iterator& __y)
  { return !__x._M_equal(__y); }

 //相等性检查
   bool
   _M_equal(const istream_iterator& __x) const
   {
 // Ideally this would just return _M_stream == __x._M_stream,
 // but code compiled with old versions never sets _M_stream to null.
       //先比较状态是否一致  //然后任一迭代器无效则相等 否则比较流指针
 return (_M_ok == __x._M_ok) && (!_M_ok || _M_stream == __x._M_stream);
   }
 //前置++运算符
     istream_iterator&
    operator++()
    {
  __glibcxx_requires_cond(_M_ok,
              _M_message(__gnu_debug::__msg_inc_istream)
              ._M_iterator(*this));//调试模式检查
  _M_read();//核心读取操作 尝试从关联流中读取下一个值 更新_M_value和迭代器状态
  return *this;//返回自身引用
    }
//operator*运算符
      const _Tp&
     operator*() const
     {
   __glibcxx_requires_cond(_M_ok,
               _M_message(__gnu_debug::__msg_deref_istream)
               ._M_iterator(*this));//调试检查
   return _M_value;
     }

​可以看到默认构造的时候迭代器的状态默认设置为false ,在std::copy中的for循环判断部分 输入流迭代器底层调用了   operator!=和_M_equal函数进行比较判断

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值