面向对象程序设计之多态性与虚函数_第1页
面向对象程序设计之多态性与虚函数_第2页
面向对象程序设计之多态性与虚函数_第3页
面向对象程序设计之多态性与虚函数_第4页
面向对象程序设计之多态性与虚函数_第5页
已阅读5页,还剩61页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、object-oriented programming in c+ 第六章 多态性与虚函数 c+ 6- 1 6.1 多态性的概念多态性的概念 多态性多态性( polymorphism ) 是面向对象程序设计的重要是面向对象程序设计的重要 特征。一个算法语言如果只支持类,而不支持多态,只能说特征。一个算法语言如果只支持类,而不支持多态,只能说 是基于对象的语言,如是基于对象的语言,如ada, vb。c+支持多态性,在支持多态性,在c+ 程序设计中能够实现多态性。利用多态性,可以设计和扩展程序设计中能够实现多态性。利用多态性,可以设计和扩展 一个易于扩展的系统。一个易于扩展的系统。 l什么叫多态?

2、什么叫多态? u多态的意思是一种事物的多种形态。多态的意思是一种事物的多种形态。 u在在c+中,中,是指具有不同功能的函数可以用同一个函数名是指具有不同功能的函数可以用同一个函数名。 u面向对象方法中一般是这样描述多态性的:向不同的对象面向对象方法中一般是这样描述多态性的:向不同的对象 发送同一个消息,不同的对象在接收时会产生不同的行为发送同一个消息,不同的对象在接收时会产生不同的行为 (即方法)。(即方法)。 c+ 6- 2 写出程序运行结果写出程序运行结果 #include #include using namespace std; class student public: studen

3、t (int n,string nam, float s) num= n; name= nam; score= s; void display( ) cout“num:”num “name:”name “score:”scoreendl; protected: int num; string name; float score; ; class graduate : public student public: graduate (int n, string nam, float s, float p): student (n,nam,s), pay(p) void display( ) co

4、ut“num:”num “name:”name “score:”score “pay:”paydisplay( ); pt = pt-display( ); c+ 6- 3 6.1 多态性的概念多态性的概念 我们其实已经接触过多态性的现象。如函数的重载我们其实已经接触过多态性的现象。如函数的重载 多态性分类多态性分类:从系统实现的角度看,多态性分为以下两类:从系统实现的角度看,多态性分为以下两类: u静态多态性:又称编译时的多态性。如函数重载属于静态静态多态性:又称编译时的多态性。如函数重载属于静态 多态性。多态性。 u动态多态性:有称为运行时的多态性。它主要表现为虚函动态多态性:有称为运行时

5、的多态性。它主要表现为虚函 数数( virtual function )。 c+ 6- 4 6.3 虚函数虚函数 能否用一个调用形式,既能调用派生类的函数,又能能否用一个调用形式,既能调用派生类的函数,又能 调用基类同名函数?调用基类同名函数?c+中的虚函数就是用来解决这一问题。中的虚函数就是用来解决这一问题。 l虚函数的作用虚函数的作用:虚函数的作用是允许在派生类中重新定义与:虚函数的作用是允许在派生类中重新定义与 基类同名的函数,并且可以通过基类指针或引用来访问基类基类同名的函数,并且可以通过基类指针或引用来访问基类 和派生类中的同名函数。和派生类中的同名函数。 c+ 6- 5 6.3 虚

6、函数虚函数 #include #include class student public: student (int n,string nam, float s) num= n; name= nam; score= s; void display( ) cout“num:”num “name:”name “score:”scoreendl; protected: int num; string name; float score; ; class graduate : public student public: graduate (int n, string nam, float s, fl

7、oat p): student (n,nam,s), pay(p) void display( ) cout“num:”num “name:”name “score:”score “pay:”paydisplay( ); / 指向基类对象指向基类对象s1 pt = pt-display( ); / 指向派生类对象指向派生类对象g1,仅仅 输出了派生类的基类数据成员,因为输出了派生类的基类数据成员,因为 它调用的是基类成员函数它调用的是基类成员函数display! 假如想输出派生类的全部数据,当然可以采用下面两种方法之一:假如想输出派生类的全部数据,当然可以采用下面两种方法之一: n通过派生类对

8、象名通过派生类对象名g1,调用派生类对象的成员函数:,调用派生类对象的成员函数:g1.display( ); n定义一个指向派生类的指针定义一个指向派生类的指针ptr,并指向,并指向g1,然后用,然后用ptr-display( )。 c+ 6- 6 6.3 虚函数虚函数 我们可以用虚函数可以顺利解决这一问题。方法是:我们可以用虚函数可以顺利解决这一问题。方法是: u在基类在基类student 中声明中声明 display函数时,在最左边加上一个关键字函数时,在最左边加上一个关键字virtual : virtual void display( ); 就可以就可以student类的类的displa

9、y函数声明为虚函数,程序其它部分不变编函数声明为虚函数,程序其它部分不变编 译运行后,可见,使用译运行后,可见,使用pt-display( ),的确将,的确将graduate类对象类对象 g1 的全部的全部 数据显示了出来,说明它调用的是数据显示了出来,说明它调用的是g1 的成员函数的成员函数 display。 u在派生类中重新定义该函数,要求函数名、函数类型、参数表完全相同。在派生类中重新定义该函数,要求函数名、函数类型、参数表完全相同。 但不加但不加virtual关键字。关键字。 u只在类里的成员函数声明时,加上关键字只在类里的成员函数声明时,加上关键字virtual,在类外定义定义虚函数

10、,在类外定义定义虚函数 时,不加时,不加virtual关键字。关键字。 u定义一个指向定义一个指向基类对象基类对象的指针,并使她指向同一类中的某一对象;的指针,并使她指向同一类中的某一对象; u通过该指针标量调用此虚函数,此时调用的就是指针变量指向的对象的同通过该指针标量调用此虚函数,此时调用的就是指针变量指向的对象的同 名函数,而不是基类的同名函数!名函数,而不是基类的同名函数! c+ 6- 7 6.3 虚函数虚函数 通过使用虚函数和指针,就能方便地调用同一类族中不同通过使用虚函数和指针,就能方便地调用同一类族中不同 类对象的同名函数,只要先用基类指针指向该对象即可。类对象的同名函数,只要先

11、用基类指针指向该对象即可。 #include #include class student public: student (int n,string nam, float s) num= n; name= nam; score= s; virtual void display( ) cout“num:”num “name:”name “score:”scoreendl; protected: int num; string name; float score; ; class graduate : public student public: graduate (int n, string

12、nam, float s, float p): student (n,nam,s), pay(p) void display( ) cout“num:”num “name:”name “score:”score “pay:”paydisplay( ); / 指向基类对象指向基类对象s1 pt = pt-display( ); / 指向派生类对象指向派生类对象g1,调调 用用g1的显示函数的显示函数display,打印出,打印出g1全全 部数据成员部数据成员 c+ 6- 8 6.3 虚函数虚函数 将函数重载与虚函数比较,可见:将函数重载与虚函数比较,可见: u函数重载是解决的是同一层次上的同名函

13、数的问题。是横函数重载是解决的是同一层次上的同名函数的问题。是横 向重载。向重载。 u虚函数解决的是不同派生层次上的同名函数的问题。相当虚函数解决的是不同派生层次上的同名函数的问题。相当 于纵向重载。于纵向重载。 u同一类族的虚函数的首部是相同的;而重载函数的首部不同一类族的虚函数的首部是相同的;而重载函数的首部不 相同(参数表不能相同)。相同(参数表不能相同)。 c+ 6- 9 6.3 虚函数虚函数 l静态关联与动态关联静态关联与动态关联 c+在编译或运行时,对多个同名函数究竟调用哪一个在编译或运行时,对多个同名函数究竟调用哪一个 函数,需要一定的机制来确定。这种确定调用的具体对象的函数,需

14、要一定的机制来确定。这种确定调用的具体对象的 过程称为过程称为“关联关联( binding )”,即把函数名与某一个类对象捆,即把函数名与某一个类对象捆 绑在一起。绑在一起。 函数重载,在编译时就可确定其调用的函数是哪一个;函数重载,在编译时就可确定其调用的函数是哪一个; 通过对象名调用虚函数,在编译时也可确定其调用的虚函数通过对象名调用虚函数,在编译时也可确定其调用的虚函数 属于哪一个类。其过程称为属于哪一个类。其过程称为“静态关联静态关联( static binding ), 因为是在运行前进行关联的,又成为早期关联因为是在运行前进行关联的,又成为早期关联( early bindin g

15、)。 c+ 6- 10 6.3 虚函数虚函数 通过指针和虚函数的结合,在编译阶段是没法进行关联的,通过指针和虚函数的结合,在编译阶段是没法进行关联的, 因为编译只作静态的语法检查,光从语句形式因为编译只作静态的语法检查,光从语句形式pt-display( )无无 法确定调用的对象,也就没法关联。法确定调用的对象,也就没法关联。 出现这种情况,我们可以在运行阶段来处理关联。在运行出现这种情况,我们可以在运行阶段来处理关联。在运行 阶段,基类指针先指向某一个对象,然后通过指针调用该对象阶段,基类指针先指向某一个对象,然后通过指针调用该对象 的成员函数。此时调用哪个函数是确定的,没有不确定因素。的成

16、员函数。此时调用哪个函数是确定的,没有不确定因素。 例如语句例如语句 pt = pt-display( ); 非常确定的是调用非常确定的是调用g1对象的对象的 成员函数成员函数display。 这种情况由于是在运行阶段将虚函数与某一类对象这种情况由于是在运行阶段将虚函数与某一类对象“绑定绑定” 在一起的,因此称为在一起的,因此称为“动态关联动态关联( dynamic binding ),或,或“滞滞 后关联后关联( late binding )”。 c+ 6- 11 6.3 虚函数虚函数 l使用虚函数,要注意使用虚函数,要注意 u只能用只能用virtual 声明类的成员函数,类外的普通函数不能

17、声明类的成员函数,类外的普通函数不能 声明成虚函数,因为它没有继承的操作。声明成虚函数,因为它没有继承的操作。 u一个成员函数被声明成虚函数后,在同一类族中的类就不一个成员函数被声明成虚函数后,在同一类族中的类就不 能再定义一个非能再定义一个非virtual的、但与该函数具有相同参数表和的、但与该函数具有相同参数表和 返回类型的同名函数。返回类型的同名函数。 u使用虚函数,系统要有一定的空间开销。当一个类带有虚使用虚函数,系统要有一定的空间开销。当一个类带有虚 函数时,编译系统会为该类构造一个虚函数表函数时,编译系统会为该类构造一个虚函数表( virtual fu nction table,

18、vtable ),它是一个指针数组,存放每个虚,它是一个指针数组,存放每个虚 函数的入口地址。函数的入口地址。 系统在进行动态关联时的时间开销是很少的,所以多系统在进行动态关联时的时间开销是很少的,所以多 态性运行效率非常高。态性运行效率非常高。 c+ 6- 12 6.3 虚函数虚函数 l什么情况下使用虚函数?什么情况下使用虚函数? u成员函数所在的类是否会作为基类?成员函数被继承后有成员函数所在的类是否会作为基类?成员函数被继承后有 没有可能发生功能变化,如果两个因素都具备,就应该将没有可能发生功能变化,如果两个因素都具备,就应该将 它声明成虚函数。它声明成虚函数。 u如果成员函数被继承后功

19、能不变,或派生类用不到该函数,如果成员函数被继承后功能不变,或派生类用不到该函数, 就不要声明成虚函数。就不要声明成虚函数。 u应考虑对成员函数的访问是通过对象名还是基类指针,如应考虑对成员函数的访问是通过对象名还是基类指针,如 果是通过基类指针或引用访问,则应当声明为虚函数。果是通过基类指针或引用访问,则应当声明为虚函数。 u有时基类中定义虚函数时并不定义它的函数体,即函数体有时基类中定义虚函数时并不定义它的函数体,即函数体 为空。其作用只是定义了一个虚函数名,具体功能留给派为空。其作用只是定义了一个虚函数名,具体功能留给派 生类添加(生类添加(6.4 节会讨论这种情况)。节会讨论这种情况)

20、。 c+ 6- 13 6.3 虚函数虚函数 l虚析构函数虚析构函数 u问题的引出:我们知道,当派生类对象撤消时,系统先调用派生类析问题的引出:我们知道,当派生类对象撤消时,系统先调用派生类析 构函数,再调用基类析构函数。但是,如果用构函数,再调用基类析构函数。但是,如果用new 运算符建立了一运算符建立了一 个派生类临时对象,但用一个基类指针指向它,当程序用带指针参数个派生类临时对象,但用一个基类指针指向它,当程序用带指针参数 的的delete 撤消对象时,会发生让人不能接受的情况:系统只析构基类撤消对象时,会发生让人不能接受的情况:系统只析构基类 对象,而不析构派生类对象:对象,而不析构派生

21、类对象: class point public: point( ) point( ) cout“析构基类对象析构基类对象”endl; ; class circle : public point public: circle( ) circle ( ) cout“析构派生类对象析构派生类对象”endl; private: int radius; ; int main( ) point *p = new circle; / 指针为指向基类对象指针,指针为指向基类对象指针, / 但实际指向临时派生类对象但实际指向临时派生类对象 delete p; return 0; c+ 6- 14 6.3 虚函数

22、虚函数 实际上,程序只析构了基类对象,而没有析构派生类实际上,程序只析构了基类对象,而没有析构派生类 对象,为什么呢?因为指针对象,为什么呢?因为指针p 为基类指针,系统认为它只有为基类指针,系统认为它只有 基类对象,而与派生类对象无关。实际上,由于该指针被指基类对象,而与派生类对象无关。实际上,由于该指针被指 向了一个临时派生类对象,所以还应该这个临时的析构派生向了一个临时派生类对象,所以还应该这个临时的析构派生 类对象。类对象。 u解决的办法:解决的办法:可以将基类的析构函数声明为虚析构函数。可以将基类的析构函数声明为虚析构函数。 如:如: virtual point( ) cout“析构

23、基类对象析构基类对象”endl; 程序其它部分不动,就行了。程序其它部分不动,就行了。 当基类的析构函数被定义成当基类的析构函数被定义成virtual,无论指针指向同,无论指针指向同 一类族中的哪一个对象,当撤消对象时,系统会采用动态关一类族中的哪一个对象,当撤消对象时,系统会采用动态关 联,调用相应的析构函数,清理该对象,然后再析构基类对联,调用相应的析构函数,清理该对象,然后再析构基类对 象。象。 c+ 6- 15 6.4 纯虚函数与抽象类纯虚函数与抽象类 前面已经提到,有时在基类中将某一成员函数定为虚前面已经提到,有时在基类中将某一成员函数定为虚 函数并不是基类本身的需要,而是派生类的需

24、要。在基类中函数并不是基类本身的需要,而是派生类的需要。在基类中 预留一个函数名,具体功能留给派生类根据需要去定义。预留一个函数名,具体功能留给派生类根据需要去定义。 在上一节中基类在上一节中基类point 中有定义面积中有定义面积area函数,是因为函数,是因为 “点点”没有面积的概念。但是,其直接派生类没有面积的概念。但是,其直接派生类circle和间接和间接 派生类派生类cylinder却都需要却都需要area 函数,而且这两个函数,而且这两个area 函数函数 的功能还不相同,一个是求圆面积,一个是求圆柱体表面积。的功能还不相同,一个是求圆面积,一个是求圆柱体表面积。 也许会想到,在基

25、类也许会想到,在基类point 中加一个中加一个area 函数,并声函数,并声 明为虚函数:明为虚函数: virtual float area ( ) const return 0; 其返回其返回0表示表示“点点”没有面积。其实,在基类中并不使没有面积。其实,在基类中并不使 用这个函数,其返回值也没有意义。用这个函数,其返回值也没有意义。 c+ 6- 16 6.4 纯虚函数与抽象类纯虚函数与抽象类 为简化起见,可以不写出这种无意义的函数体,只给出为简化起见,可以不写出这种无意义的函数体,只给出 函数的原型,并在后面加上函数的原型,并在后面加上“=0”,如:,如: virtual float a

26、rea( ) const =0 ; / 纯虚函数纯虚函数 这就将这就将area声明为一个纯虚函数声明为一个纯虚函数(pure virtual function) l纯虚函数的声明形式纯虚函数的声明形式 virtual 函数类型函数类型 函数名函数名( 参数表参数表 ) =0; 说明说明 u纯虚函数没有函数体;纯虚函数没有函数体; u最后的最后的“=0”不表示函数值返回不表示函数值返回0,它只是形式上的作用,它只是形式上的作用, 告诉编译系统:这是纯虚函数,告诉编译系统:这是纯虚函数, u这是一个声明语句,以分号结尾。这是一个声明语句,以分号结尾。 u如果基类中声明了纯虚函数,但派生类中定义该函

27、数,则该如果基类中声明了纯虚函数,但派生类中定义该函数,则该 虚函数在派生类中仍为纯虚函数。虚函数在派生类中仍为纯虚函数。 c+ 6- 17 6.4 纯虚函数与抽象类纯虚函数与抽象类 l纯虚函数的作用纯虚函数的作用 是在基类中为其派生类保留一个函数的名字,以便派是在基类中为其派生类保留一个函数的名字,以便派 生类根据需要对它进行定义。如果基类中没有保留函数名,生类根据需要对它进行定义。如果基类中没有保留函数名, 则无法实现多态性。则无法实现多态性。 c+ 6- 18 6.4 纯虚函数与抽象类纯虚函数与抽象类 l抽象类抽象类 u什么叫抽象类?什么叫抽象类?一般声明了一个类,用来定义若干对象。一般

28、声明了一个类,用来定义若干对象。 但有些类并不是用来生成对象,而是作为基类去建立派生但有些类并不是用来生成对象,而是作为基类去建立派生 类。类。这种不用来定义对象,而只作为一种基本类型用做继这种不用来定义对象,而只作为一种基本类型用做继 承的类,就叫抽象类承的类,就叫抽象类( abstract class )。由于抽象类常作。由于抽象类常作 为基类,我们也称为抽象基类为基类,我们也称为抽象基类( abstract base class )。 比如,凡是包含纯虚函数的类都叫抽象类。因为纯虚比如,凡是包含纯虚函数的类都叫抽象类。因为纯虚 函数不能被调用,包含纯虚函数的类无法建立对象。函数不能被调用

29、,包含纯虚函数的类无法建立对象。 u抽象类的作用抽象类的作用:是作为一个类族的共同基类。即,为一个:是作为一个类族的共同基类。即,为一个 类族提供一个公共接口。类族提供一个公共接口。 一个类层次结构中可以不包含任何抽象类,每一层次一个类层次结构中可以不包含任何抽象类,每一层次 的类都可以建立对象。但是,许多系统的顶层是一个抽象的类都可以建立对象。但是,许多系统的顶层是一个抽象 类,甚至顶部有好几层都是抽象类。类,甚至顶部有好几层都是抽象类。 c+ 6- 19 6.4 纯虚函数与抽象类纯虚函数与抽象类 如果由抽象类所派生出的新类中对基类的所有纯虚函如果由抽象类所派生出的新类中对基类的所有纯虚函

30、数都进行了定义,这个派生类就不是抽象类,可以被调用,数都进行了定义,这个派生类就不是抽象类,可以被调用, 成为可以用来定义对象的具体类成为可以用来定义对象的具体类( concrete class )。 如果由抽象类所派生出的新类中对基类的所有纯虚函如果由抽象类所派生出的新类中对基类的所有纯虚函 数都没有进行定义,这个派生类就仍然是抽象类。数都没有进行定义,这个派生类就仍然是抽象类。 虽然抽象类不能定义对象,但可以定义指向抽象类的虽然抽象类不能定义对象,但可以定义指向抽象类的 数据的指针。当派生类成为具体类之后,就可以用这种指针数据的指针。当派生类成为具体类之后,就可以用这种指针 向派生类对象,

31、然后通过该指针调用虚函数,实现多态性操向派生类对象,然后通过该指针调用虚函数,实现多态性操 作。作。 c+ 6- 20 编程练习:编程练习: 定义虚函数,基类为定义虚函数,基类为shape,派生出圆形、派生出圆形、 矩形、三角形类。矩形、三角形类。 c+ 6- 21 l#include lusing namespace std; l/定义抽象基类定义抽象基类shape lclass shape lpublic: l/定义纯虚函数定义纯虚函数 l; l/定义定义circle类类 lclass circle:public shape lpublic: lcircle(double r):radiu

32、s(r) l/定义虚函数定义虚函数 l protected: l double radius; /半径半径 l; l/定义定义rectangle类类 lclass rectangle:public shape lpublic: l rectangle(double w,double h):widt h(w),height(h) l/定义虚函数定义虚函数 l protected: l double width,height; l; lclass triangle:public shape lpublic: l triangle(double w,double h):width (w),heigh

33、t(h) l/定义虚函数定义虚函数 l protected: l double width,height; l; c+ 6- 22 l#include lusing namespace std; l/定义抽象基类定义抽象基类shape lclass shape lpublic: l virtual double area() const =0; /纯虚函数纯虚函数 l; l/定义定义circle类类 lclass circle:public shape lpublic: lcircle(double r):radius(r) l virtual double area() const retu

34、rn 3. 14159*radius*radius; /定义虚函数定义虚函数 l protected: l double radius; /半径半径 l; l/定义定义rectangle类类 lclass rectangle:public shape lpublic: l rectangle(double w,double h):widt h(w),height(h) l virtual double area() const return width*height; /定义虚函数定义虚函数 l protected: l double width,height; l; lclass trian

35、gle:public shape lpublic: l triangle(double w,double h):width (w),height(h) l virtual double area() const return 0.5*width*height; /定义虚函定义虚函 数数 l protected: l double width,height; l; c+ 6- 23 续:定义一个函数续:定义一个函数printarea,以对象为形参,以对象为形参 ,输出三种形状的面积,输出三种形状的面积 lint main() l l circle circle(12.6); /建立建立circl

36、e类对象类对象circl e l coutarea of circle =; l printarea(circle); /输出输出circle的面积的面积 l rectangle rectangle(4.5,8.4); /建立建立rectangle 类对象类对象rectangle l coutarea of rectangle =; l printarea(rectangle); /输出输出rectangle的面的面 积积 l triangle triangle(4.5,8.4); /建立建立triangle类对类对 象象 l coutarea of triangle =; l printar

37、ea(triangle); /输出输出triangle的面积的面积 l return 0; l c+ 6- 24 l/输出面积的函数输出面积的函数 lvoid printarea(const shape c+ 6- 25 6.4 纯虚函数与抽象类纯虚函数与抽象类 多态性把操作细节留给类的设计者(专业人员)去完多态性把操作细节留给类的设计者(专业人员)去完 成,而让编程人员(类的使用者)只需做一些宏观性的工作,成,而让编程人员(类的使用者)只需做一些宏观性的工作, 告诉系统做什么,不必考虑怎么做,简化了应用程序的编码告诉系统做什么,不必考虑怎么做,简化了应用程序的编码 工作。工作。 因此有人说,

38、多态性是开启继承功能的钥匙。因此有人说,多态性是开启继承功能的钥匙。 object-oriented programming in c+ 第七章 输入输出流 c+ 6- 27 第一章第一章 c+的初步知识的初步知识 第二章第二章 类和对象类和对象 第三章第三章 再论类和对象再论类和对象 第四章第四章 运算符重载运算符重载 第五章第五章 继承与派生继承与派生 第六章第六章 多态性与虚函多态性与虚函 数数 第七章第七章 输入输出流输入输出流 第八章第八章 c+工具工具 c+ 6- 28 7.1 c+的输入与输出的输入与输出 7.2 标准输出流标准输出流 7.3 标准出入流标准出入流 7.4 文件操

39、作与文件流文件操作与文件流 7.5 字符串流字符串流 c+ 6- 29 7.1 c+的输入和输出的输入和输出 l输入输出的含义输入输出的含义:从操作系统角度看,每一个与主机相连的输入输:从操作系统角度看,每一个与主机相连的输入输 出设备都被看做一个文件。终端键盘是输入文件,终端显示器是输出设备都被看做一个文件。终端键盘是输入文件,终端显示器是输 出文件。磁盘或光盘也可以被看作是输入输出文件。出文件。磁盘或光盘也可以被看作是输入输出文件。 u程序的输入:指的是从输入文件将数据传送给程序;程序的输入:指的是从输入文件将数据传送给程序; u程序的输出:指的是从程序将数据输出给输出文件。程序的输出:指

40、的是从程序将数据输出给输出文件。 lc+的输入输出包括以下三个方面的内容的输入输出包括以下三个方面的内容: u标准设备输入输出,从键盘输入。输出到显示器。简称标准标准设备输入输出,从键盘输入。输出到显示器。简称标准i/o。 u以外存储器文件为对象的输入输出。指从磁盘文件中输入数据,以外存储器文件为对象的输入输出。指从磁盘文件中输入数据, 将数据输出到磁盘文件中。简称文件将数据输出到磁盘文件中。简称文件i/o u对内存中指定的空间进行输入输出。通常指定一个字符数组作为对内存中指定的空间进行输入输出。通常指定一个字符数组作为 存储空间,它称为字符串输入输出,简称串存储空间,它称为字符串输入输出,简

41、称串i/o c+采取了不同的方法,实现这三种输入输出。采取了不同的方法,实现这三种输入输出。 c+ 6- 30 7.1 c+的输入和输出的输入和输出 lc+输入输出流输入输出流 c+的输入输出流是指由若干字节组成的字节序列。在的输入输出流是指由若干字节组成的字节序列。在 内存中,系统为每一个数据流开辟一个缓冲区,用来存放流内存中,系统为每一个数据流开辟一个缓冲区,用来存放流 中的数据。中的数据。 u当使用当使用 cout 和插入符和插入符“”从输入缓冲区提取数据,从输入缓冲区提取数据, 送给程序中的相关变量。送给程序中的相关变量。 总之,内存缓冲区中的数据就是流。总之,内存缓冲区中的数据就是流

42、。 c+ 6- 31 7.1 c+的输入和输出的输入和输出 l流类与流对象流类与流对象:在:在c+中,输入输出流被定义成类,中,输入输出流被定义成类,c+的的 i /o 库中的类称为流类库中的类称为流类( stream class )。用流类定义的对象。用流类定义的对象 称为流对象。称为流对象。 cout 和和 cin 并不是并不是c+提供的语句,它们是提供的语句,它们是iostream 类的对象。正如类的对象。正如c+没有提供赋值语句,只提供了赋值表达没有提供赋值语句,只提供了赋值表达 式(表达式后面加分号,形成语句)。式(表达式后面加分号,形成语句)。 在在 iostream 头文件中重载

43、运算符:头文件中重载运算符:“”在在c +中是位移运算符,由于在中是位移运算符,由于在 iostream 头文件中对它们进行头文件中对它们进行 了重载,使它们能用做标准输入输出运算符,所以,在用它了重载,使它们能用做标准输入输出运算符,所以,在用它 们的程序中必须使用们的程序中必须使用 #include 语句把语句把iostream 类包含到程序中。类包含到程序中。 下面我们来看看下面我们来看看 i/o 类库中类的情况。类库中类的情况。 c+ 6- 32 7.1 c+的输入和输出的输入和输出 li/o 类库中常用的流类类库中常用的流类 strstream strstream strstream

44、 输入字符串流类输入字符串流类 输出字符串流类输出字符串流类 输入输出字符串流类输入输出字符串流类 istrstream ostrstream strstream fstream fstream fstream 输入文件流类输入文件流类 输出文件流类输出文件流类 输入输出文件流类输入输出文件流类 ifstream ofstream fstream iostream iostream iostream 通用输入流和其他输入流的基类通用输入流和其他输入流的基类 通用输出流和其他输出流的基类通用输出流和其他输出流的基类 通用输入输出流和其他输入输出流的基通用输入输出流和其他输入输出流的基 类类 is

45、tream ostream iostream iostream抽象基类抽象基类ios 在哪个头文件中声明在哪个头文件中声明作作 用用类类 名名 c+ 6- 33 7.1 c+的输入和输出的输入和输出 liostream 文件中定义的文件中定义的4种流对象种流对象 stderrostream_withassign屏幕屏幕标准错误标准错误 流流 clog stderrostream_withassign屏幕屏幕标准错误标准错误 流流 cerr stdoutostream_withassign屏幕屏幕标准输出标准输出 流流 cout stdinistream_withassign键盘键盘标准输入标准

46、输入 流流 cin c语言对应文件语言对应文件对应的类对应的类对应设对应设 备备 含义含义对象对象 说明说明 n cin 是是 istream 的派生类的派生类 istream_withassign 的对象,它是从键盘输入的对象,它是从键盘输入 数据流到内存;数据流到内存; n cout 是是 ostream 的派生类的派生类 ostream_withassign 的对象,它从内存输的对象,它从内存输 出数据流到显示器;出数据流到显示器; n cerr 和和 clog 相似,均为向显示器输出出错信息相似,均为向显示器输出出错信息 c+ 6- 34 7.2 标准输出流标准输出流 ii.clog

47、流对象流对象:也是标准出错流,是:也是标准出错流,是console log 的缩写。的缩写。 作用和作用和 cerr 相同。区别在于,相同。区别在于,cerr 不经过缓冲区,直接不经过缓冲区,直接 向显示器输出,而向显示器输出,而 clog 经过缓冲区,缓冲区装满后或遇经过缓冲区,缓冲区装满后或遇 到到endl 时,向显示器输出。时,向显示器输出。 #include #include void main( ) float a,b,c,disc; cout abc; if (a=0) cerr a is equal to zero,error!endl; else if (disc=b*b-4*

48、a*c)0) cerr disc=b*b-4*a*c 0 endl; else coutx1=(-b+sqrt(disc)/(2*a)endl; coutx2=(-b-sqrt(disc)/(2*a)endl; c+ 6- 35 7.2 标准输出流标准输出流 l格式输出格式输出:输出数据时,为简便起见往往不指定输出格式。但也可以:输出数据时,为简便起见往往不指定输出格式。但也可以 指定输出格式。指定输出格式。 输出格式有两种方法:输出格式有两种方法: 使用控制符输出格式;使用控制符输出格式; 使用流对使用流对 象的有关成员函数。象的有关成员函数。 u使用控制符输出格式使用控制符输出格式:这些控

49、制符是在头文件:这些控制符是在头文件 iomanip中定义。程序中定义。程序 必须包含该头文件。必须包含该头文件。 控控 制制 符符作作 用用 dec, hex, oct分别设置整数的基数为分别设置整数的基数为10、16、8 setbase (n)设置整数基数为设置整数基数为n ( n 为为10,16,8三者之一三者之一) setfill ( c ) 设置填充字符设置填充字符c,c是字符常量或变量是字符常量或变量 setprecision (n)设置实数精度为设置实数精度为n位位 setw (n)设置字段宽度为设置字段宽度为 n setiosflags (ios:left / right )输

50、出数据左输出数据左/右对齐右对齐 resetioflags ( )终止已设置的输出格式,在括号中指定内容终止已设置的输出格式,在括号中指定内容 c+ 6- 36 7.2 标准输出流标准输出流 #include #include int main( ) int a=123; char *pt = “china”; double pi = 22.0/7.0; cout “dec:”decaendl; cout “hec:”hexaendl; cout “oct:”octaendl; coutsetw(10)ptendl; coutsetfill(*)setw(10)ptendl; cout“pi=

51、”piendl; cout“pi=“setprecision(4)piendl; cout“pi=“setiosflags (ios:fixed) piendl; return 0; c+ 6- 37 7.2 标准输出流标准输出流 l用流成员函数用流成员函数 put 输出字符输出字符 我们已经知道,程序中一般用我们已经知道,程序中一般用 cout 实现输出,实现输出,c out 流在内存中有相应的缓冲区。流在内存中有相应的缓冲区。 有时,我们想只输出一个字符,有时,我们想只输出一个字符,ostream 类提供了类提供了 put 成员函数来满足这一特殊需求:成员函数来满足这一特殊需求: #inc

52、lude int main( ) char *a = basic; for (int i=4; i=0; i-) cout.put(*(a+i); cout.put(n); return 0; c+ 6- 38 7.3 标准输入流标准输入流 lcin 流流:cin 流是流是 istream类的对象。它从标准输入设备(键盘)获取数类的对象。它从标准输入设备(键盘)获取数 据,程序中的变量通过流提取符据,程序中的变量通过流提取符“”从流中提取数据。从流中提取数据。 l用于字符输入的流成员函数:用于字符输入的流成员函数:用用get 函数读入一个字符:函数读入一个字符:get函数有三中函数有三中 形式

53、:无参数、有一个参数和形式:无参数、有一个参数和3个参数的形式。个参数的形式。 u不带参数的不带参数的 get 函数函数:其调用形式为:其调用形式为:cin.get ( ) #include int main( ) char c; cout“input a sentence,please: endl; while (c=cin.get()!=eof) coutc; return 0; c+ 6- 39 7.3 标准输入流标准输入流 u有一个参数的有一个参数的get函数:调用形式为:函数:调用形式为:cin.get(ch)。作用。作用 是从输入流中读取一个字符,并赋给变量是从输入流中读取一个字符

54、,并赋给变量 ch 。 #include int main( ) char c; coutinpute a sectence,please: endl; while (cin.get(c) cout.put(c); coutendendl; return 0; c+ 6- 40 7.3 标准输入流标准输入流 u有三个参数的有三个参数的 get 函数:调用形式:函数:调用形式: cin.get ( 字符数组字符数组/字符指针,字符个数字符指针,字符个数n,终止字符,终止字符) 其作用是,从输入流中读取其作用是,从输入流中读取 n-1 个字符,并赋给指定的数个字符,并赋给指定的数 组(或指针指向的

55、数组)。如果在读取中遇见终止字符,则提组(或指针指向的数组)。如果在读取中遇见终止字符,则提 前结束读取操作。前结束读取操作。 #include int main( ) char ch20; coutinpute a sectence,please: endl; cin.get(ch,18,n); coutchendl; return 0; c+ 6- 41 7.3 标准输入流标准输入流 l用成员函数用成员函数getline 读入一行字符。调用形式:读入一行字符。调用形式: getline ( 字符数组字符数组/字符指针字符指针, 字符个数字符个数n, 终止字符终止字符 ) 作用是从读入流中读

56、取一行字符,并赋给字符数组。作用是从读入流中读取一行字符,并赋给字符数组。 c+ 6- 42 7.3 标准输入流标准输入流 listream 类其它成员函数类其它成员函数 ueof 函数函数:表示文件结束。从输入流中读入文件,当达到:表示文件结束。从输入流中读入文件,当达到 文件尾,则文件尾,则eof函数值为非函数值为非0(表示真),否则为(表示真),否则为0 (假假)。 #include int main( ) char c; while (!cin.eof() if (c=cin.get()!= ) cout.put(c); else cout.put(_); return 0; c+ 6

57、- 43 7.4 文件操作与文件流文件操作与文件流 l文件的概念文件的概念:文件一般是指存储在外部介质上的数据集合。:文件一般是指存储在外部介质上的数据集合。 我们主要是指数据文件。根据数据的组织形式,数据文件分我们主要是指数据文件。根据数据的组织形式,数据文件分 为:为: uascii文件文件:又称为文本文件,它的每一个字节放一个:又称为文本文件,它的每一个字节放一个as cii代码,代表一个字符。代码,代表一个字符。 u二进制文件二进制文件:又称为内部格式文件,它把数据按其在内存:又称为内部格式文件,它把数据按其在内存 中的存储形式远洋输出到磁盘上存放。中的存储形式远洋输出到磁盘上存放。

58、对于字符信息,在内存中是以对于字符信息,在内存中是以ascii代码形式存放的,代码形式存放的, 因此,无论用因此,无论用ascii文件输出,还是用二进制文件输出,其文件输出,还是用二进制文件输出,其 数据形式是一样的。但是对于数值数据,二者是不同的。数据形式是一样的。但是对于数值数据,二者是不同的。 c+ 6- 44 7.4 文件操作与文件流文件操作与文件流 l文件流类与文件流对象文件流类与文件流对象:文件流是以外存文件为输入输出对象的:文件流是以外存文件为输入输出对象的 数据流。数据流。 u输出文件流输出文件流是从内存流向外存文件的数据;是从内存流向外存文件的数据; u输入文件流输入文件流是

59、从外存文件流向内存的数据。是从外存文件流向内存的数据。 u文件与文件流的区别文件与文件流的区别:文件流不是文件,而只是以文件为输入:文件流不是文件,而只是以文件为输入 输出对象的流。输出对象的流。 uc+定义的文件类定义的文件类: n ifstream 类,是类,是 istream 类派生,用于从磁盘文件输入数类派生,用于从磁盘文件输入数 据。据。 n ofstream 是是ostream 类派生,用于向磁盘文件的输出。类派生,用于向磁盘文件的输出。 n fstream 是是 iostream 类的派生,用于磁盘文件的输入输出。类的派生,用于磁盘文件的输入输出。 c+ 6- 45 7.4 文件

60、操作与文件流文件操作与文件流 l文件的打开与关闭文件的打开与关闭 u打开磁盘文件打开磁盘文件:打开文件是指在文件读写之前做必要的准备工:打开文件是指在文件读写之前做必要的准备工 作。包括:作。包括: n 为文件对象和指定的磁盘文件建立关联,以便使文件流流向为文件对象和指定的磁盘文件建立关联,以便使文件流流向 指定的磁盘文件;指定的磁盘文件; n 指定文件的工作方式,如,是读文件还是写文件,是操作指定文件的工作方式,如,是读文件还是写文件,是操作a scii文件还是二进制文件。文件还是二进制文件。 打开文件有两种实现方式:打开文件有两种实现方式: u调用文件流的成员函数调用文件流的成员函数ope

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论