`
bsr1983
  • 浏览: 1117613 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

第九章 运算符重载及流类库

 
阅读更多

第9章 运算符重载及流类库

9.1 运算符重载

9.1.1 重载对象的赋值运算符

 编译器在默认情况下为每个类生成一个默认的赋值操作,用于同类的两个对象之间相互赋值。默认的含义是逐个为成员赋值,即将一个对象的成员的值赋给另一个对象相应的成员,这种赋值方式对于有些类可能是不正确的。

C++的关键字“operator”和运算符一起使用就表示一个运算符函数。例如“operator +”表示重载“+”运算符。

9.1.2 运算符重载的实质

C++是由函数组成的,在C++内部,任何运算都是通过函数来实现的。因为任何运算都是通过函数来实现的,所以运算符重载其实就是函数重载,要重载某个运算符,只要重载相应的函数就可以了。与以往稍有不同的是,需要使用新的关键字“operator”,它和C++的一个运算符连用,构成一个运算符函数名,例如“operator+.通过这种构成方法就可以像重载普通函数那样重载运算符函数operator+()。由于C++已经为各种基本数据类型定义了该运算函数,所以只需要为自己定义的类型重载operator+()就可以了。

一般地,为用户定义的类型重载运算符都要求能够访问这个类型的私有成员,所以只有两条路可走:要么将运算符重载为这个类型的成员函数,要么将运算符重载为这个类型的友元。

C++的运算符大部分都可以重载,不能重载的只有:: 和 ?: 。前面三个是因为在C++中都有特定的含义,不准重载以避免不必要的麻烦;“?:”则是因为不值得重载。另外,“sizeof”和“#”不是运算符,因而不能重载,而=、()、[ ] ->4个运算符只能用类运算符来重载。

9.1.3 <<>>++运算符重载实例

其实,插入符“<<”和提取符“>>”的重载也与其他运算符重载一样,但操作符的左边是流对象的别名而不是被操作的对象,运算符跟在流对象的后面,它们要直接访问类的私有数据,而且流是标准类库,用户只能继承不能修改,更不能是流库的成员,所以它们必须作为类的友元重载。

插入符函数的一般形式如下:

ostream &operator<<(ostream & output,类名 &对象名)

{

return output;

}

output是类ostream对象的引用,它是cout的别名,即ostream&output=cout。调用参数时,output引用cout(即cout的别名)。显然,插入符函数的第2个参数使用引用方式比直接使用对象名的可读性要好一些。

提取符函数的一般形式如下:

istram &operator>>(istream & input,类名&对象名)

{

return input;

}

input是类istream对象的引用。它是cin的别名,即istream&input=cin。调用参数时,input引用cin(即cin的别名)。

另外,提取符函数需要返回新的对象值,所以应该使用引用,即“类名&对象名”,不能使用“类名 对象名”。插入符函数不改变对象的值,所以两种方法都可以。

显然,运算符“<<”重载函数有两个参数,第1个是ostream类的一个引用,第2个是自定义类型的一个对象。这个重载方式是友元重载。这个函数的返回类型是一个ostream类型的引用,在函数中实际返回的是该函数的第一个参数,这样做是为了使得“<<”能够连续使用。

有些C++编译器不区分前缀或后缀运算符,这时只能通过对运算符函数进行重载来反映其为前缀或后缀运算符。

注意不能自己定义新的运算符,只能是把C++原有的运算符用到自己设计的类上面去。同时,经过重载,运算符并不改变原有的优先级,也不改变它所需的操作数目。当不涉及到定义的类对象时,它仍然执行系统预定义的运算,只有用到自己定义的对象上,才执行新定义的操作。

应该根据需要进行运算符重载。不排除在某些特殊情况下会有一些特殊的需要,但大多数情况下不会将运算符“+”重载为两个复数相减的运算(尽管有能力这么做)。一般总是要求运算符重载合乎习惯。

9.1.4 类运算符和友元运算符的区别

如果运算符所需的操作数(尤其是第一个操作数)希望进行隐式类型转换,则运算符应通过友元来重载。另一方面,如果一个运算符的操作需要修改类对象的状态,则应当使用类运算符,这样更符合数据封装的要求。但参数是引用还是对象,则要根据运算符在使用中可能出现的情况来决定。

如果对象作为重载运算符函数的参数,则可以使用构造函数将常量转换成该类型的对象。如果使用引用作为参数,因为这些常量不能作为对象名使用,所以编译系统就要报错。

9.1.5 下标运算符“[ ]”的重载

运算符“[ ]”只能用类运算符来重载。

9.2 流类库

C++的流类库由几个进行I/O操作的基础类和几个支持特定种类的源和目标的I/O操作的类组成。

9.2.1 流类库的基础类

C++中,输入输出时同流来完成的。C++的输出操作将一个对象的状态转换成一个字符序列,输出到某个地方。输入操作也是从某个地方接收到一个字符序列,然后将其转换成一个对象的状态所要求的格式。这看起来很像数据在流动,于是把接收输出数据的地方叫做目标,把输入数据来自的地方叫做源。而输入和输出操作可以看成字符序列在源、目标以及对象之间的流动。C++将与输入和输出有关的操作定义为一个类体系,放在一个系统库里,以备用户调用。这个执行输入和输出操作的类体系就叫做流类,提供这个流类实现的系统库就叫做流类库。C++的流类库由几个进行I/O操作的基础类和几个支持特定种类源和目标的I/O操作类组成。

C++中,如果在多条继承路径上有一个汇合处,则称这个汇合处的基类为公共基类(ios符合条件)。因为可以通过不同的访问路径访问这个基类,从而使公共的基类会产生多个实例,这样会引起二义性。如果想使这个公共的基类只产生一个实例,则可以将这个基类说明为虚基类。ios类就是isrream类和ostream类的虚基类,用来提供对流进行格式化I/O操作和错误处理的成员函数。用关键字virtual可将公共基类说明为虚基类,虚基类的定义很难处理,这就是为什么最初的C++语言没有能支持多重继承的原因。

ios类公有派生的istreamostream两个类分别提供对流进行提取操作和插入操作的成员函数,而iostream类通过组合istream类和ostream类来支持对一个流进行双向(也就是输入和输出)操作,它并没有提供新的成员函数。

C++流类库预定义了4个流,它们是cincoutcerrclog。事实上,可以将cin视为类istream的一个对象,而将cout视为类ostream的对象。

流是一个抽象概念,当实际进行I/O操作时,必须将流和一种具体的物理设备(比如键盘)联接起来。C++的流类库预定义的4个流所联接起来的具体设备为:

cin 与标准输入设备相联接

cout 与标准输出设备相联接

cerr 与标准错误输出设备相联接(非缓冲方式)

clog 与标准错误输出设备相联接(缓冲方式)

9.2.2 默认输入输出的格式控制

关于数值数据,默认方式能够自动识别浮点数并用最短的格式输出,还可以将定点数分成整数和小数部分。

特别要注意字符的读入规则。对单字符来讲,它将舍去空格,直到读到字符为止。对于单字符对象abc,“cin>>a>>b>>c;”能将连续的3个字符分别正确地赋给相应对象。

对字符串来讲,它从读到第一个字符开始,到空格符结束。对于字符数组,使用数组名来整体读入。但对于字符指针,尽管为它动态分配了地址,也只能采取逐个赋值的方法,它不仅不以空格结束,反而舍弃空格(读到字符才计数)。因为字符串没有结束位,所以将字符串作为整体输出时,有效字符串后面将出现乱码。不过,可以手工增加表示字符串的结束符“\0”来消除乱码。

当用键盘同时给一个单字符对象和一个字符串对象赋值时,不要先给字符串赋值。如果先给它赋值,应该强行使用结束符。

Bool(布尔型)VC6.0中把输入的0识别为false,其他的值均识别为1。输出时,只有01两个值。如果默认输入输出格式不能满足自己的要求,就必须重载它们。

9.2.3 使用ios_base

1.ios_base类简介

ios_base类派生ios类,ios类又是istream类和ostream类的虚基类。

9.1 常量名及含义

常量名

含义

 skipws

 跳过输入中的空白

 left

 输出数据按输出域左边对齐输出

 right

 输出数据按输出域右边对齐输出

 intermal

 在指定任何引导标志或基之后填充字符

 dec

 转换基数为十进制形式

 oct

 转换基数为八进制形式

 hex

 转换基数为十六进制形式

 showbase

 输出带有一个表示制式的字符

 showpoint

 浮点输出时必须带有一个小数点和尾部的0

 uppercase

 十六进制数值输出使用大写A~F,科学计数显示使用大写字母E

 showpos

 在正数前添加一个“+”号

 scientific

 使用科学计数法表示浮点数

 fixed

 使用定点形式表示浮点数

 untibuf

 每次插入之后,ostream::osfx刷新该流的缓冲区。默认缓冲单元为cerr

 boolalpha

 把逻辑值输出为truefalse(否则输出为10

 adjustfield

 对齐方式域(与leftrightinternal配合使用,例如ios_base::adjustfield

 basefield

 数字方式域(与dexocthex配合使用,例如ios_base::basefield

 floatfield

 浮点方式域(与fixscientific配合使用,例如ios_base::floatfield

9.2 处理标志的成员函数及其作用

成员函数

作     用

 long flagslong

 允许程序员设置标志字的值,并返回以前所设置的标志字

 long flags()

 仅返回当前的标志字

 long setf(long,long)

 用于设置标志字的某一位,第2个参数指定所要操作的位,第1个参数指定为该参数所设置的值

 long setf(long)

 用来设置参数指定的标志位

 long unsetf(long)

 清除参数指定的标志位

 int width(int)

 返回以前设置显示数据的域宽

 int width()

 只返回当前域宽(默认宽度为0

 char fill(char)

 设置填充字符,设置的宽度小时,空余的位置用填充字符来填充,默认条件下是空格。这个函数返回以前设置的填充字符

 char fill()

 获得当前的填充字符

 int precision(int)

 返回以前设置的精度(小数点后的小数位数)

 int precision

 返回当前设置的精度

2.直接使用格式控制

9.1中的名字可以直接用在系统提供的输入输出流中,并且有些是成对的。加no前缀标识取消原操作。

width(int)设置宽度的效果只对一次输入或输出有效,在完成一次输入或输出之后,宽度设置自动恢复为0(表示按实际数据宽度输入输出)。在设置输入时,实际输入的字符串最大长度为n-1(宽度n计入结束符)setw(int)只对紧跟其后的输出有效。

3.使用成员函数

在使用成员函数进行格式控制的时候,setf用来设置,unsetf用来恢复默认设置。它们被流对象调用,使用表9.1的常量设置。

9.3 文件流

C++里,文件操作是通过流来完成的。C++总共有输入文件流、输出文件流和输入输出文件流3种,并已将它们标准化。

要打开一个输入文件流,需要定义一个ifstream类型的对象;要打开一个输出文件流,需要定义一个ofstream类型的对象;如果要打开输入输出文件流,则要定义一个fstream类型的对象。这3种类型都定义在头文件<fstream>里。

9.3.1 使用文件流

流类具有支持文件的能力,在建立和使用文件时,就像使用cincout一样方便。

可以总结出对文件进行操作的方法如下:

(1)打开一个相应的文件流.

(2)把这个流和相应的文件关联起来。

因为ifstreamofstreamfstream3个类都具有自动打开文件的构造函数,而这个构造函数就具有open()的功能。因此,事实上可以用一条语句:

ofstream myStream("myText.txt");

来完成上述两步。如果指定文件路径,路径中的“\”号必须使用转义字符表示。

(3)操作文件流。

综上所述,流是I/O流类的中心概念。流是一种抽象,它负责在数据的生产者和消费者之间建立联系并管理数据的流动,程序将流对象看做是文件对象的化身。

一个输出流对象是信息流动的目标,ofstream是最重要的输出流。一个输入流对象是数据流动的源头,ifstream是最重要的输入流。一个iostream对象可以是数据流动的源或目的,fstream就是从它派生的。

其实,文件流的对象是字节流,而且文本文件和二进制文件均是字节流。将文件与流关联起来,就是使用“>>”和“<<”对文件进行读写。但不能使用空格,这是由“>>”和“<<”原来的性质所决定的。

9.3.2 几个典型流成员函数

1.输出流的open函数

open函数的原型如下:

void open(char const *,int filemode,int =filebuf::openprot);

它有3个参数,第一个是要打开的文件名,第2个是文件的打开方式,第3个是文件的保护方式,一般都使用默认值。第2个参数可以取如下所示的值:

ios_base::in 打开文件进行读操作,这种方式可避免删除现存文件的内容

ios_base::out 打开文件进行写操作,这是默认模式

ios_base::ate 打开一个已有的输入或输出文件并查找到文件尾

ios_base::app 打开文件以便在文件尾部添加数据

ios_base::binary 指定文件以二进制方式打开,默认为文本方式

ios_base::trunc 如文件存在,将其长度截断为零并清除原有内容

ios_base::app方式之外,在其他几种方式下,文件刚刚打开是,指示当前读写位置的文件指针都定位于文件的开始位置,而ios_base::app使文件当前的写指针定位于文件尾。这几种方式也可以通过“或”运算符“|”同时使用。

2.输入流类的open函数

使用默认构造函数建立对象,然后调用open成员函数打开文件。

输入流的打开模式如下所示:

ios_base::in 打开文件用于输入(默认)

ios_base::binary 指定文件以二进制方式打开,默认为文本方式

3.close成员函数

close成员函数用来关闭与一个文件流相关联的磁盘文件。如果没有关闭该文件,因为文件流是对象,所以在文件流对象的生存期结束时,将自动关闭该文件。不过,应养成及时关闭不再使用的文件的习惯,以避免误操作或者干扰而产生的错误。

4.错误处理函数

在对一个流对象进行I/O操作时,可能会产生错误。当错误发生时,可以使用文件流的错误处理成员函数进行错误类型判别。这些成员函数的作用见表9.3

可以使用这些函数检查当前流的状态,例如:

if(cin.good())  cin>>data;

函数clear更多地是用于在已知流发生错误的情况下清除流的错误状态,也可以用于设置流的错误状态。除非发生致命错误(hardfail),否则可以使用函数clear清除流的错误状态。

“!”运算符已经过了重载,它与fail函数执行相同的功能,因此表达式if(!cout)等价于if(cout.fail())if(cout)等价于if(!cout.fail())

9.3 成员函数及其功能

函  数

功   能

 bad()

 如果进行非法操作,返回true,否则返回false

 clear()

 设置内部错误状态,如果用缺省参量调用则清除所有错误位

 eof()

 如果提取操作已经到达文件尾,则返回true,否则返回false

 good()

 如果没有错误条件和没有设置文件结束标志,返回true,否则返回false

 fail()

 与good相反,操作失败返回false,否则返回true

 is_open()

 判定流对象是否成功地与文件关联,若是,返回true,否则返回false

9.3.3 文件存取综合实例 

分享到:
评论

相关推荐

    C 程序设计课件:第九章 流类库和输入输出.ppt

    第九章的焦点是C++程序设计中的流类库和输入输出机制。C++语言并不直接支持输入输出语句,而是依赖于一个强大的流类库,该库是C++标准库的核心部分,与标准模板库同等重要。数据在不同对象间的传输被抽象为“流”,...

    C++ PPT课件。。。。

    第一章 C++概述,第二章 数据类型、运算符与表达式,第三章 简单的输入输出,第四章 C++...第十章 构造函数和析构函数,第十一章 继承和派生类,第十二章 类的其它特性,第十三章 运算符重载,第十四章 输入/输出流类库

    C++ 程序设计课件:第九章 流类库和输入输出.ppt

    第九章的焦点是C++中的流类库及其在输入输出中的应用。C++不直接提供输入/输出语句,而是采用一个内置的I/O流类库,它是C++标准库的关键部分,与标准模板库同等重要。这个库通过抽象概念“流”来处理数据的传输,使...

    C++第九章习题解答

    这些流对象可以通过重载&gt;&gt;和运算符来执行输入和输出操作。 流类库中还定义了输入输出格式控制符,它是一个公有的无名枚举类型,每一个量对应两个字节数据的一位,每一个位代表一种控制。这些格式控制符可以通过类...

    谭浩强C++程序设计复习提纲.pdf

    **第八章:运算符重载和流类库** 理解运算符重载的原理和使用场景,以及如何自定义运算符。学习C++的I/O流库,包括iostream、fstream等,掌握如何使用cin、cout、ifstream和ofstream进行文件输入输出。 **第九章:...

    C++程序设计实验考试大纲

    4. **运算符重载和流类库**:学习如何重载各类运算符,了解流类的基本结构,熟悉预定义流、文件流的使用,掌握格式控制和错误处理。 5. **模板**:深入理解函数模板、类模板,以及模板与继承的结合使用。 6. **...

    c++语言程序设计课后习题答案

    9. **第九章 群体类**:群体类通常指的是容器类,如vector、list、set等,它们用于管理和操作一组对象,是STL(标准模板库)的一部分。 10. **第十章 群体数据的组织**:这部分内容可能涵盖了STL中的其他容器,如map...

    2010C++考试大纲

    4. **运算符重载和流类库**:掌握不同类型的运算符重载,理解流类的基本结构,预定义的流,文件流,格式控制和错误处理。 5. **模板**:学习函数模板和类模板,以及模板与继承的交互。 6. **程序设计基础**:了解虚...

    《C++程序设计》电子教案

    8. **第9章 多态性与虚函数** - 多态性:同一接口,不同实现,是面向对象的重要特性。 - 虚函数:通过虚函数实现动态绑定,实现多态。 - 抽象基类和纯虚函数:定义接口,支持多态性。 9. **第10章 流类库与输入...

    c++语言程序设计(第三版)

    第9章 群体类 9.1 线性群体 9.2 C++标准库中的容器类 9.3 小结 习题 第10章 群体数据的组织 10.1 插入排序 10.2 选择排序 10.3 交换排序 10.4 顺序查找 10.5 折半查找 10.6 标准C++库中的算法 10.7 小结 习题 第...

    陈天华面向对象程序设计C第三第一章推选PPT文档.ppt

    在C++中,面向对象特性包括类(Class)、对象(Object)、继承(Inheritance)、派生(Derivation)、友元(Friend)、虚函数(Virtual Function)、运算符重载(Operator Overloading)、I/O流类库(I/O Stream ...

    C++课件(谭浩强版)

    6. **第7章 运算符重载**:C++允许对已有的运算符进行重载,使其能适用于自定义的数据类型,使得代码更加自然和易读。 7. **第8章 继承和派生类**:这部分会讲解如何通过继承来实现代码的复用,以及派生类的特性,...

    MFC实战参考程序

    第1章 MFC应用程序概述 1 1.1 Windows应用程序概述 2 1.2 消息 2 1.2.1 消息结构 3 1.2.2 消息类型 3 1.2.3 消息队列 4 1.2.4 消息循环 6 ...2.5 方法重载和运算符重载 53 2.5.1 方法重载 53 2

    -C++参考大全(第四版) (2010 年度畅销榜

    第9章 文件I/O 9.1 C与C++的文件I/O 9.2 流和文件 9.3 流 9.4 文件 9.5 文件系统基础 9.6 fread()和fwrite() 9.7 fseek()和随机访问I/O 9.8 fprintf()和fscanf() 9.9 标准流 第10章 预处理器和注释 10.1 预处理器 ...

    C++程序设计实践教程1

    第九章阐述运算符重载,第十章介绍函数模板和类模板,第十一章讲解异常处理和断言,第十二章则涵盖了C++的流和类库。第十三章通过建立赌场对象模型来实践面向对象编程。 本书特色在于其内容新颖、通俗易懂,注重...

Global site tag (gtag.js) - Google Analytics