Cha*_*l72 3 c++ iostream istream-iterator istream
我已经使用<<stream运算符为对象实现了反序列化例程.例程本身使用an istreambuf_iterator<char>逐个从流中提取字符,以构造对象.
最终,我的目标是能够使用a迭代流istream_iterator<MyObject>并将每个对象插入到vector.漂亮的标准,除了我遇到麻烦istream_iterator,以停止时,如果击中结束流迭代.现在,它只是永远循环,即使调用istream::tellg()表明我在文件的末尾.
这是重现问题的代码:
struct Foo
{
Foo() { }
Foo(char a_, char b_) : a(a_), b(b_) { }
char a;
char b;
};
// Output stream operator
std::ostream& operator << (std::ostream& os, const Foo& f)
{
os << f.a << f.b;
return os;
}
// Input stream operator
std::istream& operator >> (std::istream& is, Foo& f)
{
if (is.good())
{
std::istreambuf_iterator<char> it(is);
std::istreambuf_iterator<char> end;
if (it != end) {
f.a = *it++;
f.b = *it++;
}
}
return is;
}
int main()
{
{
std::ofstream ofs("foo.txt");
ofs << Foo('a', 'b') << Foo('c', 'd');
}
std::ifstream ifs("foo.txt");
std::istream_iterator<Foo> it(ifs);
std::istream_iterator<Foo> end;
for (; it != end; ++it) cout << *it << endl; // iterates infinitely
}
Run Code Online (Sandbox Code Playgroud)
我知道在这个简单的例子中我甚至不需要istreambuf_iterator,但我只是想简化问题,所以人们更有可能回答我的问题.
所以这里的问题是即使istreambuf_iterator到达流缓冲区的末尾,实际流本身也不会进入EOF状态.调用istream::eof()返回false,即使istream::tellg()返回文件中的最后一个字节,istreambuf_iterator<char>(ifs)并将true与之比较istreambuf_iterator<char>(),这意味着我肯定在流的末尾.
我查看了IOstreams库代码,以确定它是如何确定是否istream_iterator处于最终位置,并且基本上它检查是否istream::operator void*() const评估为true.这个istream库函数只返回:
return this->fail() ? 0 : const_cast<basic_ios*>(this);
Run Code Online (Sandbox Code Playgroud)
换句话说,0如果设置了failbit ,则返回(false).然后,它将此值与默认构造的实例中的相同值进行比较,istream_iterator以确定我们是否在最后.
所以std::istream& operator >> (std::istream& is, Foo& f)当istreambuf_iterator比较true到end迭代器时,我尝试在我的例程中手动设置failbit .这非常有效,并且正确地终止了循环.但现在我真的很困惑.似乎istream_iterator 肯定会检查std::ios::failbit以表示"流结束"条件.但这不是什么意思std::ios::eofbit?我认为failbit是错误条件,例如,如果fstream无法打开基础文件或其他东西.
那么,为什么我需要调用istream::setstate(std::ios::failbit)才能终止循环?
使用istreambuf_iterator时,您正在操作istream对象的基础streambuf对象.streambuf对象对它的所有者(istream对象)一无所知,因此在streambuf对象上调用函数不会对istream对象进行更改.这就是当你到达eof时没有设置istream对象中的标志的原因.
做这样的事情:
std::istream& operator >> (std::istream& is, Foo& f)
{
is.read(&f.a, sizeof(f.a));
is.read(&f.b, sizeof(f.b));
return is;
}
Run Code Online (Sandbox Code Playgroud)
编辑
我在调试器中单步执行代码,这就是我找到的.istream_iterator有两个内部数据成员.指向关联的istream对象的指针,以及模板类型的对象(在本例中为Foo).当你调用++它时,它会调用这个函数:
void _Getval()
{ // get a _Ty value if possible
if (_Myistr != 0 && !(*_Myistr >> _Myval))
_Myistr = 0;
}
Run Code Online (Sandbox Code Playgroud)
_Myistr是istream指针,_Myval是Foo对象.如果你看这里:
!(*_Myistr >> _Myval)
Run Code Online (Sandbox Code Playgroud)
这就是它所谓的操作员>>过载.它叫操作员!在返回的istream对象上.正如你在这里看到的,运营商!如果设置了failbit或badbit,则只返回true,eofbit不会这样做.
那么,接下来会发生什么,如果设置了failbit或badbit,则istream指针变为NULL.下次将迭代器与结束迭代器进行比较时,它会比较istream指针,它们都是NULL.