《C++Primer》函数 标准IO库

● 如果想直接改变实参的值,可以使用引用形参,从C语言背景转到C++的程序猿习惯通过指针来实现对实参的访问。在C++中,使用引用形参则更安全和自然。如果使用引用形参的唯一目的是避免复制实参,则应将形参定义为const。应该将不需要修改的引用形参定义为const引用。

 

● 从避免复制vector的角度出发,应考虑将形参声明为引用类型。事实上,C++程序猿倾向于通过传递指向容器中需要处理的元素的迭代器来传递容器。

vector<int>::const_iterator beg

 

● 通常,将数组形参直接定义为指针要比使用数组语法定义要好。这样就明确地表示,函数操纵的是指向数组元素的指针,而不是数组本身。

当编译器检查数据形参关联的实参时,它只会检查实参是不是指针、指针的类型和数组元素的类型是否匹配,而不会检查数组的长度。

如果是通过引用传递数组的话,数组大小会成为形参和实参类型的一部分,编译器会检查数组实参的大小和形参的大小是否匹配。

void printValues(int (&arr)[10]);

多维数组的传递:

void printValues(int (arr*)[10]);

传递指向数组第一个和最后一个元素的下一个位置的指针。

void printValues(const int *beg, const int *end);

显示传递表示数组大小的形参

void printValues(const int ia[], size_t size);

 

● 返回非引用类型,返回的是一个临时对象,复制了要返回的对象。

   返回引用类型,返回的是对象的引用,千万不能返回局部变量的引用,返回的引用是一个左值,可以直接对其修改,如果不希望被修改,返回值应该声明为const。

千万不要返回指向局部变量的指针!

 

● 默认实参,如果一个形参具有默认实参,那么,它后面所有的形参都必须有默认实参。

 

● 静态局部变量一旦被创建,在程序结束前都不会被撤销。

 

● 在函数返回类型前加上关键字inline就可以将函数指定为内联函数。

一般来说,内联机制适用于优化小的,只有几行的而且经常被调用的函数。

内联函数应该在头文件中定义。

 

● 编译器隐式地将在类内定义的成员函数当做内联函数。

类的成员函数可以访问该类的private成员。

每个成员函数(除了static成员函数外),都有一个额外的、隐含的形参this。

bool sameIsbn(const Sales_item &rhs) const

{

        this->isbn == rhs.isbn;

}

用这种方式使用const的函数称为常量成员函数。由于this是指向const对象的指针,const成员函数不能修改调用该函数的对象。

const对象、指向const对象的指针或引用只能用于调用其const成员函数,如果尝试用他们来调用非const成员函数,则是错误的。

 

● 构造函数:

Sales_item():units_sold(0),revenue(0.0) {}

在冒号和花括号之间的代码称为构造函数的初始化列表。

如果没有为一个类显式定义任何构造函数,编译器将自动为这个类生成默认构造函数。

 

● 重载函数:

如果具有相同的名字而形参表不同,则称为重载函数。

如果两个函数声明的返回类型和形参表完全匹配,则将第二个函数声明视为第一个的重复声明。

函数不能基于不同的返回类型而实现重载。

可基于函数的引用形参是指向const对象还是指向非const对象,实现函数重载。将引用形参定义为const来重载函数是合法的。

同样可基于指针指向const对象还是非const对象,实现函数重载,但是不能基于指针本身是否为const来实现函数的重载。

 

● 指向函数的指针:

bool (*pf)(const string &, const string &);

typedef简化定义:

bool (*pf)(const string &, const string &);

 

● IO标准库类型和头文件

image

简单的iostream继承层次:

 

image

  • 以上所讲的流类型(stream class)读写的是由char类型组成的流。除此之外,标准库还定义了一组相关的类型,支持wchar_t类型。即,每个类名前都加上“w”前缀,以此与char类型的版本区别开来。

  • IO对象不允许复制和赋值:因此流对象不可以存储在vector或其他容器里;函数形参和返回类型也不可以是流类型。

 

● 标准IO 库管理一系列条件状态(condition state)成员,用来标记给定的 IO 对象是否处于可用状态,或者碰到了哪种特定的错误。如下列出了标准库定义的一组函数和标记,提供访问和操纵流状态的手段。

strm::iostate               这是机器相关的整型别名,代表一种类型,由各个io类定义,用于定义条件状态(strm在这里代表各个流类型)。

strm::badbit                strm::iostate类型的值,用于指出被破坏的流。

strm::failbit                 strm::iostate类型的值,用于指出失败的 IO 操作。

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

s.eof()                        返回eof标志位的值,真或假。

s.fail()                        返回fail标志位的值,真或假。

s.bad()                      返回bad标志位的值,真或假。

s.good()                    检测流s的有效性,当eof,fail,bad都不为真时good标志位有效,调用good()函数返回真值。

s.clear()                    将流s中的所有状态值都重设为有效状态。

s.clear(flag)               将流s中的某个指定条件状态置为有效,flag是strm::iostate类型的条件状态,若括号内不写具体条件状态则将所有条件状态全部设置有效。

s.setstate(flag)          给流添加指定条件,设置flag位为触发状态,flag是strm::iostate类型对象。

s.rdstate()                 返回流s的当前条件,返回值类型为strm::iostate。

 

  • 所有流对象都包含一个条件状态成员,他是strm::iostate类型的对象,以二进制位的形式使用。

  • failbit, eofbit, badbit和goodbit是四个strm::iostate类型的常量值,他们本身分别表示一种条件状态,具体如下表:

  • image

  • badbit, eofbit, failbit,goodbit构成了四个基本流状态。

  • 用cout检测goodbit, badbit, eofbit, failbit的值分别是0,1,2,4,与上面的表格正好完全对应

  • (goodbit:0000 0000;  badbit:0000 0001;eofbit:0000 0010; failbit:0000 0100)。

  • 可使用clear和setstate来操作和管理条件状态成员,他们常与badbit,eofbit,failbit,goodbit以及位或“|”结合起来使用。

  • 用bad(), fail(), eof()和good()操作可以检测流状态是否属于,还可以用rdstate()来返回整个条件状态成员。

 

if(FileStream.rdstate() == ios::eofbit)

       cout << “End of file!/n”;

    if(FileStream.rdstate() == ios::badbit)

       cout << “Fatal I/O error!/n”;

    if(FileStream.rdstate() == ios::failbit)

       cout << “Non-fatal I/O error!/n”;

    if(FileStream.rdstate() == ios::goodbit)

cout << “No errors!/n”;

 

● 输出缓冲区的管理:

每一个IO对象管理一个缓冲区,用于存储程序读写的数据。缓冲区的刷新会使其中的内容写入到真实的输出设备或文件中。

以下情况都会导致缓冲区被刷新:

  • 程序正常结速时,将清空所有输出缓冲区。

  • 在一些不确定的时候,缓冲区可能已经满了,这种情况下,缓冲区将会在写入下一个值之前刷新缓冲区。

  • 用操纵符(flush, ends,endl)显式地刷新缓冲区:

cout<<“Hi!”<<flush; //仅仅刷新缓冲区,不插入任何其他字符

cout<<“Hi!”<<ends; //插入空格字符null,并刷新缓冲区

cout<<“Hi!”<<endl; //插入换行符,并刷新缓冲区

 

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

cout<<unitbuf<<“first”<<“second”<<“third”<<nounitbuf; //等价于cout<<“first”<<flush<<“second”<<flush<<“third”<<flush;

 

输出应多使用endl,而非 \n

 

  • 使用tie函数将输出流与输入流关联起来,当输入流与输出流绑定在一起的时候,任何输入操作都将首先刷新其输出流关联的缓冲区。

tie函数可以由istream或ostream对象调用,使用一个ostream流对象的指针做形参。

  • cin.tie(&cout);//将cin和cout绑定在一起,cin>>ival;时将导致cout所关联的缓冲区被刷新。

  • ostream *old_tie=cin.tie(); //不传递参数时,返回当前绑定的对象

  • cin.tie(0); 接受0作为参数时,解除当前绑定

 

● 文件的输入和输出:

需要读写文件时,必须定义自己的对象,并将它们绑定在需要的文件上:

ifstream infile(ifile.c_str());

ofstream outfile(outfile.c_str());

 

 

ifstream infile;

ofstream outfile;

infile.open(“in.txt”);

outfile.open(“out.txt”);

 

IO标准库使用C风格字符串而不是C++ string类型的字符串作为文件名。

 

打开文件后,检验打开是否成功:

if(!infile)

{

     cerr<<”error:unable to open input file”<<endl;

}

 

fstream对象一旦打开,就保持与制定的文件相关联。如果要把fstream对象与另一个不同的文件相关联,则必须先关闭现在的文件,然后打开另一个文件。

ifstream infile(“in.txt”);

infile.close();

infile.open(“next.txt”);

 

清除文件流的状态:

如果读写操作失败了,对象的状态将保持为错误模式,直到执行clear操作重新恢复流的状态为止。关闭流并不能改变流对象的内部状态,调用clear后,就像重新创建了对象一样。

如果程序猿需要重用文件流读写多个文件,必须在读另一个文件之前调用clear清除该流的状态。

 

● 你应当知道filename表示文件的名称(一个字符串),而新出现的则是open_mode(打开模式)。open_mode的值用来定义以怎样的方式打开文件。下面是打开模式的列表:

image

默认是,与ifstream流对象关联的文件将以in模式打开,该模式允许文件做读的操作;与ofstream关联的文件则以out模式打开,使文件可写。以out模式打开的文件会被清空,丢弃该文件存储的所有数据。

当文件同时以in和out打开时不清空。如果只使用out模式,不使用in模式,则文件会清空已存在的数据。如果打开文件制定了trunc模式,无论是否同时指定了in模式,文件同样会被清空。

 

模式是文件的属性而不是流的属性。

 

 

ifstream &openFile(ifstream &in, const string &file)

{

     in.close();

     in.clear();

     in.open(file.c_str());

     return in;

}

本文链接:http://www.alonemonkey.com/cplus-review-three.html