C++ Primer:第八章:IO库

本文详细介绍了C++ IO库的使用,包括IO类、条件状态管理、输出缓冲管理及流对象关联等核心内容。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

IO库学习笔记,不是很全面。


一、IO类

先看一张表:



标准库通过继承机制来定义了一组面向对象类,这些由继承机制实现的类都享有共通的一些接口。因此,我们通常可以将一个派生类对象当做它的基类对象来使用。

由上图可以看出我们的IO库继承图。


标准库分别定义了三个独立的头文件:<iostream>、<fstream>、<sstream>

<iostream>定义了读写流的基本类型,一般从窗口读写;<fstream>定义了读写命名文件的类型;<sstream>定义了读写内存string对象的类型。而且<fstream>中的每种类型和<sstream>中定义的类型都是从<iostream>头文件中定义的相关类型派生而来的。


为了支持宽字符的语言,标准库定义了一组类型和对象来操纵wchar_t类型的数据、例如:wcin、wcout、wcerr是分别对应cin、cout、cerr的宽字符版本,宽字符版本对象和类型与其对应的char类型版本定义在同一个头文件中。例如,fstream头文件定义了ifstream和wifstream类型。


1. IO对象无拷贝或赋值

ofstream out1, out2;
out1  =  out2;  // 错误,不能对流对象赋值
ofstream print(ofstream); // 错误,不能初始化流参数
out2  = print(out2); // 错误,不能拷贝流对象

所以我们一般都是使用流对象的引用或指针来操作流对象。


2. 条件状态

strm::iostate

机器相关的整型名,由各个iostream类定义,用于定义条件状态

strm::badbit

strm::iostate类型的值,用于指出被破坏的流[1Linux32]

strm::failbit

strm::iostate类型的值,用于指出失败的IO操作[4]

strm::eofbit

strm::iostate类型的值,用于指出流已经到达文件结束符[2]

s.eof()

如果设置了流 s的 eofbit,则该函数返回true

s.fail()

如果设置了流 s的 failbit值或badbit,则该函数返回true

s.bad()

如果设置了流 s的 badbit,则该函数返回true

s.good()

如果流 s处于有效状态,则该函数返回true

s.clear()

将流 s中的所有状态值都重设为有效状态

s.clear(flag)

将流 s中的某个指定条件状态设置为有效flag的类型是strm::iostate

s.setstate(flag)

给流 s添加指定条件。flag的类型是strm::iostate

s.rdstate()

返回流 s的当前条件,返回值类型为strm::iostate



int ival;
cin >> ival;
假如我们输入Boo,该操作就会失败,因为我们期待输入一个int,而输入的却是一个字符B。这样cin就会进入错误状态。类似的,如果我们输入文件结束标识,cin也会进入错误状态。一个流一旦进入错误状态,后续操作都会失败。

确定一个流对象的状态最简单的办法是将它作为一个条件使用:

while (cin >> ival)
    //
如果cin操作没有失败,我们就可以进行后续操作。

查询流状态:

IO库定义了一个与机器无关的iostate类型,它提供了表达流状态的完整功能:

badbit表示系统级错误,如不可恢复的读写错误,通常情况下,一旦badbit被置位,流就无法再使用了。在发生可恢复的错误后,failbit被置位,如期望读取数值却读出一个字符的错误。如果到达文件尾,eofbit和failbit都会被置位。goodbit的值为0,表示流未发生错误。badbit、failebit和eofbit任一个被置位,则检测流状态的条件会失败。


标准库还定义了一组函数来查询这些标志位的状态。good函数在所有错误位均未被置位时返回true,而bad、fail和eof函数则对应错误位被置位时返回true。此外,在badbit被置位时,fail也会返回true。


管理条件状态:

流对象rfstate()成员返回一个iostate值,对应流当前状态。setstate操作将给定的条件位置位,表示发生了对应错误。clear成员是一个重载的成员:它有一个不接受参数的版本,另一个是接受一个iostate类型的参数。

clear不接受参数的版本清除(复位)所有错误标志位,执行clear()后,调用good()会返回true,可以这样使用这些成员:

	istream::iostate old_state = cin.rdstate(); // 记住cin当前状态
	cin.clear(); // 使cin有效,good()返回true
	process_input(cin); // 使用cin
	cin.setstate(old_state); // 将cin置为原有状态

带参数的clear版本接受一个iostate值,表示流的新状态。为了复位单一的条件状态位,首先用rdstate()读出当前条件状态,然后用位操作将所需位复位来生成新的状态:

// 复位failbit和badbit,保持其他位不变
cin.clear(cin.rdstate() & ~cin.failbit & ~cin.badbit);

3. 管理输出缓冲

每个输出流都管理一个缓冲区,用来保存程序读写的数据:

os  << "please enter a value: ";

这个字符串可能立即打印出来,也可能被操作系统保存在缓冲区,随后再打印。


导致缓冲区刷新(即,数据真正的写到输出设备或文件)的原因有很多:

(1). 程序正常结束,作为main函数的return操作的一部分,缓冲刷新被执行;

(2). 缓冲区满时,需要撒互信缓冲,而后新的数据才能继续写入缓冲区;

(3). 使用操纵符如endl来显示刷新缓冲区;

(4). 在每个输出操作之后,我们可以用操纵符unitbuf设置流的内部状态,来清空缓冲区。默认情况下,cerr是设置unitbuf的,因此写到cerr的内容都是立即刷新的;

(5). 一个输出流可能被关联(tie)到另一个流。在这种情况下,当读写被关联的流时,关联到的流的缓冲区会被刷新。例如,cin和cerr都关联到cout,因此,读cin或写cerr时都会导致cout的缓冲区被刷新。


刷新输出缓冲区:

我们已经知道了endl操纵符,它完成换行并刷新缓冲区,IO库还定义了两个类似的操纵符:flush和ends。flush刷新缓冲区,但不输出任何字符,ends向缓冲区插入一个空字符,然后刷新缓冲区。

cout << "hi!" << endl; // 输出hi!和一个换行,然后刷新缓冲区
cout << "hi!" <<flush; //输出flush,然后刷新缓冲区,不附加任何额外字符
cout << "hi!" <<; //输出hi!和一个空字符,然后刷新缓冲区

unitbuf操纵符:如果想在每次输出操作后都刷新缓冲区,可以使用unitbuf。它告诉流在接下来的每次写操作后都进行一次flush操作,而nounitbuf操作符则重置流,使其恢复使用正常的系统管理的缓冲区刷新机制。

cout << unitbuf; // 所有的输出操作后都会立即刷新缓冲区
/*
*/
cout << nounibuf; //回到正常的缓冲方式

警告:如果程序崩溃,输出缓冲区不会被刷新

程序异常终止,输出缓冲区是不会刷新的。当一个程序崩溃后,它所输出的数据很可能停留在输出缓冲区中等待打印。

所以,当调试一个已经崩溃了的程序时,必须确认那些你认为已经输出的数据确实输出了。否则,可能会将大量的程序浪费在追踪代码为什么没有执行,而实际上只是程序崩溃后缓冲区没被刷新而已。


关联输入和输出流:

当一个输入流被关联到一个输出流时,任何试图从输入流读取数据的操作都会先刷新关联的输出流,标准库将coutcin绑在一起,因此语句:

cin >> ival;
导致cout的缓冲区被刷新。

交互式系统通常来说应该将输入和输出流关联起来,这意味着,所有的输出包括用户提示信息,都会在读操作之前被打印出来。

tie有两个重载的版本:一个版本不带参数,返回指向输出流的指针,如果本对象当前关联到一个输出流,则返回的就是指向这个输出流的指针。如果对象未关联到输出流,则返回空指针。第二个版本接受一个指向ostream对象的指针,将自己关联到这个ostream对象。

std::ostream* tie() const; // 不带参数版本,返回指向当前绑定的输出流的指针,若未关联到输出流,返回空指针
std::ostream* tie(std::ostream* tiestr); // 将tiestr指向的输出流绑定到该对象,并返回上一个绑定的指针


可以将一个istream对象关联到一个ostream对象,也可以关联到另一个ostream对象:

    cin.tie(&cout);
    ostream *old_tie = cin.tie(nullptr); // cin不再与其他流关联
    cin.tie(&cerr);	//将cin与cerr绑定,读取cin会刷新cerr而不是cout
    cin.tie(old_tie);	//将cin与cout重新绑定

为了彻底解开流的关联,我们可以传递一个空指针nullptr。每个流同时最多关联到一个流,但多个流可以同时关联到一个ostream。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值