环境: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函数进行比较判断