欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

第八章

程序员文章站 2022-07-12 18:02:54
...

8.1

IO 对象不可复制或赋值
ofstream out1, out2;
out1 = out2; // error: cannot assign stream objects
// print function: parameter is copied
ofstream print(ofstream);
out2 = print(out2); // error: cannot copy stream objects

        这个要求有两层特别重要的含义,只有支持复制的元素类型可以存储在 vector 或其他容器类型里。由于流对象不能复制,因此不能存储在 vector(或其他)容器中(即不存在存储流对象的 vector 或其他容器)。第二个含义是:形参或返回类型也不能为流类型。如果需要传递或返回 IO对象,则必须传递或返回指向该对象的指针或引用:

ofstream &print(ofstream&); // ok: takes a reference,
no copy
while (print(out2)) { /* ... */ } // ok: pass reference to out2 

        一般情况下,如果要传递 IO 对象以便对它进行读写,可用非 const 引用的方式传递这个流对象。对 IO 对象的读写会改变它的状态,因此引用必须是非const 的。

8.2

条件状态

        所有流对象都包含一个条件状态成员,该成员由 setstate (置1)和 clear(置0) 操作管理。这个状态成员为 iostate 类型,这是由各个 iostream 类分别定义的机器相关的整型。该状态成员以二进制位(bit)的形式使用

        badbit 标志着系统级的故障,如无法恢复的读写错误。如果出现了这类错误,则该流通常就不能再继续使用了。如果出现的是可恢复的错误,如在希望获得数值型数据时输入了字符, 此时则设置 failbit 标志, 这种导致设置 failbit的问题通常是可以修正的。eofbit 是在遇到文件结束符时设置的,此时同时还设置了 failbit。

        流的状态由 bad、fail、eof 和 good 操作提示。如果 bad、fail 或者 eof中的任意一个为 true,则检查流本身将显示该流处于错误状态。类似地,如果这三个条件没有一个为 true,则 good 操作将返回 true。

        clear 和 setstate 操作用于改变条件成员的状态。clear 操作将条件重设为有效状态。在流的使用出现了问题并做出补救后,如果我们希望把流重设为有效状态,则可以调用 clear 操作。使用 setstate 操作可打开某个指定的条件,用于表示某个问题的发生。除了添加的标记状态,setstate 将保留其他已存在的状态变量不变

8.3输出缓冲区管理

        下面几种情况将导致缓冲区的内容被刷新,即写入到真实的输出设备或者文件:

1. 程序正常结束。作为 main 返回工作的一部分,将清空所有输出缓冲区。
2. 在一些不确定的时候,缓冲区可能已经满了,在这种情况下,缓冲区将会在写下一个值之前刷新。
3. 用操纵符(第 1.2.2 节)显式地刷新缓冲区,例如行结束符 endl。
4. 在每次输出操作执行完后,用 unitbuf 操作符设置流的内部状态,从而清空缓冲区。

5. 可将输出流与输入流关联(tie)起来。在这种情况下,在读输入流时将刷新其关联的输出缓冲区。

        第一个经常使用的flush,用于刷新流,但不在输出中添加任何字符。第二个则是比较少用的ends,这个操纵符在缓冲区中插入空字符null,然后刷新它:

cout << "hi!" << flush; // flushes the buffer; adds no data
cout << "hi!" << ends; // inserts a null, then flushes the buffer
cout << "hi!" << endl; // inserts a newline, then flushes the buffer

        如果需要刷新所有输出,最好使用 unitbuf 操纵符。这个操纵符在每次执行完写操作后都刷新流:

cout << unitbuf << "first" << " second" << nounitbuf;

        等价于:

cout << "first" << flush << " second" << flush;

        nounitbuf 操纵符将流恢复为使用正常的、由系统管理的缓冲区刷新方式。

        如果程序崩溃了,则不会刷新缓冲区。如果仅因为缓冲区没有刷新,程序员将浪费大量的时间跟踪调试并没有执行的代码。基于这个原因,输出时应多使用 endl 而非 '\n'。使用endl 则不必担心程序崩溃时输出是否悬而未决(即还留在缓冲区,未输出到设备中)。

8.4

C++ 中的文件名

        如果调用open 或使用文件名作初始化式,需要传递的实参应为 C 风格字符串,而不是标准库 strings 对象。 通常,比较好的方法是将文件名读入 string 对象,而不是 C 风格字符数组。假设要使用的文件名保存在 string 对象中,则可调用 c_str 成员(第4.3.2 节)获取 C 风格字符串。

// construct an ifstream and bind it to the file named ifile
ifstream infile(ifile.c_str());
// ofstream output file object to write file named ofile
ofstream outfile(ofile.c_str());

8.5

stringstream 对象的和使用

string line, word; // will hold a line and word from input,respectively
while (getline(cin, line)) { // read a line from the input into line
// do per-line processing
istringstream stream(line); // bind to stream to the line we read
while (stream >> word){ // read a word from line
// do per-word processing
}
}
stringstream 提供的转换和/或格式化

        例如, 有一个数值型数据集合, 要获取它们的 string 表示形式,或反之。sstream 输入和输出操作可自动地把算术类型转化为相应的string 表示形式,反过来也可以。

int val1 = 512, val2 = 1024;
ostringstream format_message;
// ok: converts values to a string representation
format_message << "val1: " << val1 << "\n"<< "val2: " << val2 << "\n";

        这里创建了一个名为 format_message 的 ostringstream 类型空对象,并将指定的内容插入该对象。重点在于 int 型值自动转换为等价的可打印的字符串。format_message 的内容是以下字符:val1: 512\nval2: 1024

        相反,用 istringstream 读 string 对象,即可重新将数值型数据找回来。读取 istringstream 对象自动地将数值型数据的字符表示方式转换为相应的算术值。 

// str member obtains the string associated with a stringstream
istringstream input_istring(format_message.str());
string dump; // place to dump the labels from the formatted message
// extracts the stored ascii values, converting back to arithmetic types
input_istring >> dump >> val1 >> dump >> val2;
cout << val1 << " " << val2 << endl; // prints 512 1024
        因为输入操作符读取的是有类型的值,因此读入的对象类型必须和由stringstream 读入的值的类型一致。在本例中,input_istring 分成四个部分:string 类型的值 val1,接着是 512,然后是 string 类型的值 val2,最后是1024。一般情况下,使用输入操作符读 string 时,空白符将会忽略。于是,在读与 format_message 关联的 string 时,忽略其中的换行符。