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

下载本文档

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

文档简介

面向对象程序设计之多态性与虚函数6.1多态性的概念 多态性(polymorphism)是面向对象程序设计的重要特征。一个算法语言如果只支持类,而不支持多态,只能说是基于对象的语言,如Ada,VB。C++支持多态性,在C++程序设计中能够实现多态性。利用多态性,可以设计和扩展一个易于扩展的系统。什么叫多态?多态的意思是一种事物的多种形态。在C++中,是指具有不同功能的函数可以用同一个函数名。面向对象方法中一般是这样描述多态性的:向不同的对象发送同一个消息,不同的对象在接收时会产生不同的行为(即方法)。 第2页,共63页,2024年2月25日,星期天写出程序运行结果#include<iostream>#include<string>Usingnamespacestd;classstudent{public:student(intn,stringnam,floats){num=n;name=nam;score=s;}voiddisplay(){cout<<“num:”<<num<<“name:”<<name<<“score:”<<score<<endl;}protected:intnum;stringname;floatscore;};classgraduate:publicstudent{public:graduate(intn,stringnam,floats,floatp):student(n,nam,s),pay(p){}voiddisplay(){cout<<“num:”<<num<<“name:”<<name<<“score:”<<score<<“pay:”<<pay<<endl;}private:floatpay;};voidmain(){students1(1001,”Li”,98.5);graduateg1(2001,”Liu”,90.5,800.5);student*pt=&s1;pt->display();pt=&g1;pt->display();}

第3页,共63页,2024年2月25日,星期天6.1多态性的概念 我们其实已经接触过多态性的现象。如函数的重载多态性分类:从系统实现的角度看,多态性分为以下两类:静态多态性:又称编译时的多态性。如函数重载属于静态多态性。动态多态性:有称为运行时的多态性。它主要表现为虚函数(virtualfunction)。第4页,共63页,2024年2月25日,星期天6.3虚函数

能否用一个调用形式,既能调用派生类的函数,又能调用基类同名函数?C++中的虚函数就是用来解决这一问题。虚函数的作用:虚函数的作用是允许在派生类中重新定义与基类同名的函数,并且可以通过基类指针或引用来访问基类和派生类中的同名函数。第5页,共63页,2024年2月25日,星期天6.3虚函数#include<iostream.h>#include<string.h>classstudent{public:student(intn,stringnam,floats){num=n;name=nam;score=s;}voiddisplay(){cout<<“num:”<<num<<“name:”<<name<<“score:”<<score<<endl;}protected:intnum;stringname;floatscore;};classgraduate:publicstudent{public:graduate(intn,stringnam,floats,floatp):student(n,nam,s),pay(p){}voiddisplay(){cout<<“num:”<<num<<“name:”<<name<<“score:”<<score<<“pay:”<<pay<<endl;}private:floatpay;};voidmain(){students1(1001,”Li”,98.5);graduateg1(2001,”Liu”,90.5,800.5);student*pt=&s1;pt->display();//指向基类对象s1pt=&g1;pt->display();//指向派生类对象g1,仅输出了派生类的基类数据成员,因为它调用的是基类成员函数display!}假如想输出派生类的全部数据,当然可以采用下面两种方法之一:通过派生类对象名g1,调用派生类对象的成员函数:g1.display();定义一个指向派生类的指针ptr,并指向g1,然后用ptr->display()。第6页,共63页,2024年2月25日,星期天6.3虚函数 我们可以用虚函数可以顺利解决这一问题。方法是:在基类student中声明display函数时,在最左边加上一个关键字virtual:

virtualvoiddisplay();

就可以student类的display函数声明为虚函数,程序其它部分不变编译运行后,可见,使用pt->display(),的确将graduate类对象g1的全部数据显示了出来,说明它调用的是g1的成员函数display。在派生类中重新定义该函数,要求函数名、函数类型、参数表完全相同。但不加virtual关键字。只在类里的成员函数声明时,加上关键字virtual,在类外定义定义虚函数时,不加virtual关键字。定义一个指向基类对象的指针,并使她指向同一类中的某一对象;通过该指针标量调用此虚函数,此时调用的就是指针变量指向的对象的同名函数,而不是基类的同名函数!第7页,共63页,2024年2月25日,星期天6.3虚函数 通过使用虚函数和指针,就能方便地调用同一类族中不同类对象的同名函数,只要先用基类指针指向该对象即可。#include<iostream.h>#include<string.h>classstudent{public:student(intn,stringnam,floats){num=n;name=nam;score=s;}

virtualvoiddisplay(){cout<<“num:”<<num<<“name:”<<name<<“score:”<<score<<endl;}protected:intnum;stringname;floatscore;};classgraduate:publicstudent{public:graduate(intn,stringnam,floats,floatp):student(n,nam,s),pay(p){}voiddisplay(){cout<<“num:”<<num<<“name:”<<name<<“score:”<<score<<“pay:”<<pay<<endl;}private:floatpay;};voidmain(){students1(1001,”Li”,98.5);graduateg1(2001,”Liu”,90.5,800.5);student*pt=&s1;pt->display();//指向基类对象s1pt=&g1;pt->display();//指向派生类对象g1,调用g1的显示函数display,打印出g1全部数据成员}第8页,共63页,2024年2月25日,星期天6.3虚函数 将函数重载与虚函数比较,可见:函数重载是解决的是同一层次上的同名函数的问题。是横向重载。虚函数解决的是不同派生层次上的同名函数的问题。相当于纵向重载。同一类族的虚函数的首部是相同的;而重载函数的首部不相同(参数表不能相同)。第9页,共63页,2024年2月25日,星期天6.3虚函数静态关联与动态关联

C++在编译或运行时,对多个同名函数究竟调用哪一个函数,需要一定的机制来确定。这种确定调用的具体对象的过程称为“关联(binding)”,即把函数名与某一个类对象捆绑在一起。 函数重载,在编译时就可确定其调用的函数是哪一个;通过对象名调用虚函数,在编译时也可确定其调用的虚函数属于哪一个类。其过程称为“静态关联(staticbinding),因为是在运行前进行关联的,又成为早期关联(earlybinding)。第10页,共63页,2024年2月25日,星期天6.3虚函数 通过指针和虚函数的结合,在编译阶段是没法进行关联的,因为编译只作静态的语法检查,光从语句形式pt->display()无法确定调用的对象,也就没法关联。 出现这种情况,我们可以在运行阶段来处理关联。在运行阶段,基类指针先指向某一个对象,然后通过指针调用该对象的成员函数。此时调用哪个函数是确定的,没有不确定因素。例如语句pt=&g1;pt->display();非常确定的是调用g1对象的成员函数display。 这种情况由于是在运行阶段将虚函数与某一类对象“绑定”在一起的,因此称为“动态关联(dynamicbinding),或“滞后关联(latebinding)”。第11页,共63页,2024年2月25日,星期天6.3虚函数使用虚函数,要注意只能用virtual声明类的成员函数,类外的普通函数不能声明成虚函数,因为它没有继承的操作。一个成员函数被声明成虚函数后,在同一类族中的类就不能再定义一个非virtual的、但与该函数具有相同参数表和返回类型的同名函数。使用虚函数,系统要有一定的空间开销。当一个类带有虚函数时,编译系统会为该类构造一个虚函数表(virtualfunctiontable,vtable),它是一个指针数组,存放每个虚函数的入口地址。 系统在进行动态关联时的时间开销是很少的,所以多态性运行效率非常高。第12页,共63页,2024年2月25日,星期天6.3虚函数什么情况下使用虚函数?成员函数所在的类是否会作为基类?成员函数被继承后有没有可能发生功能变化,如果两个因素都具备,就应该将它声明成虚函数。如果成员函数被继承后功能不变,或派生类用不到该函数,就不要声明成虚函数。应考虑对成员函数的访问是通过对象名还是基类指针,如果是通过基类指针或引用访问,则应当声明为虚函数。有时基类中定义虚函数时并不定义它的函数体,即函数体为空。其作用只是定义了一个虚函数名,具体功能留给派生类添加(6.4节会讨论这种情况)。第13页,共63页,2024年2月25日,星期天6.3虚函数虚析构函数问题的引出:我们知道,当派生类对象撤消时,系统先调用派生类析构函数,再调用基类析构函数。但是,如果用new运算符建立了一个派生类临时对象,但用一个基类指针指向它,当程序用带指针参数的delete撤消对象时,会发生让人不能接受的情况:系统只析构基类对象,而不析构派生类对象:classpoint{public:point(){}~point(){cout<<“析构基类对象”<<endl;}};classcircle:publicpoint{public:circle(){}~circle(){cout<<“析构派生类对象”<<endl;}private:intradius;};intmain(){point*p=newcircle;

//指针为指向基类对象指针,

//但实际指向临时派生类对象deletep;return0;}第14页,共63页,2024年2月25日,星期天6.3虚函数 实际上,程序只析构了基类对象,而没有析构派生类对象,为什么呢?因为指针p为基类指针,系统认为它只有基类对象,而与派生类对象无关。实际上,由于该指针被指向了一个临时派生类对象,所以还应该这个临时的析构派生类对象。解决的办法:可以将基类的析构函数声明为虚析构函数。如:

virtual~point(){cout<<“析构基类对象”<<endl;}

程序其它部分不动,就行了。 当基类的析构函数被定义成virtual,无论指针指向同一类族中的哪一个对象,当撤消对象时,系统会采用动态关联,调用相应的析构函数,清理该对象,然后再析构基类对象。第15页,共63页,2024年2月25日,星期天6.4纯虚函数与抽象类 前面已经提到,有时在基类中将某一成员函数定为虚函数并不是基类本身的需要,而是派生类的需要。在基类中预留一个函数名,具体功能留给派生类根据需要去定义。 在上一节中基类point中有定义面积area函数,是因为“点”没有面积的概念。但是,其直接派生类circle和间接派生类cylinder却都需要area函数,而且这两个area函数的功能还不相同,一个是求圆面积,一个是求圆柱体表面积。 也许会想到,在基类point中加一个area函数,并声明为虚函数:

virtualfloatarea()const{return0;}

其返回0表示“点”没有面积。其实,在基类中并不使用这个函数,其返回值也没有意义。第16页,共63页,2024年2月25日,星期天6.4纯虚函数与抽象类 为简化起见,可以不写出这种无意义的函数体,只给出函数的原型,并在后面加上“=0”,如:

virtualfloatarea()const=0;//纯虚函数 这就将area声明为一个纯虚函数(purevirtualfunction)纯虚函数的声明形式

virtual函数类型函数名(参数表)=0; 说明纯虚函数没有函数体;最后的“=0”不表示函数值返回0,它只是形式上的作用,告诉编译系统:这是纯虚函数,这是一个声明语句,以分号结尾。如果基类中声明了纯虚函数,但派生类中定义该函数,则该虚函数在派生类中仍为纯虚函数。第17页,共63页,2024年2月25日,星期天6.4纯虚函数与抽象类纯虚函数的作用 是在基类中为其派生类保留一个函数的名字,以便派生类根据需要对它进行定义。如果基类中没有保留函数名,则无法实现多态性。第18页,共63页,2024年2月25日,星期天6.4纯虚函数与抽象类抽象类什么叫抽象类?一般声明了一个类,用来定义若干对象。但有些类并不是用来生成对象,而是作为基类去建立派生类。这种不用来定义对象,而只作为一种基本类型用做继承的类,就叫抽象类(abstractclass)。由于抽象类常作为基类,我们也称为抽象基类(abstractbaseclass)。 比如,凡是包含纯虚函数的类都叫抽象类。因为纯虚函数不能被调用,包含纯虚函数的类无法建立对象。抽象类的作用:是作为一个类族的共同基类。即,为一个类族提供一个公共接口。 一个类层次结构中可以不包含任何抽象类,每一层次的类都可以建立对象。但是,许多系统的顶层是一个抽象类,甚至顶部有好几层都是抽象类。第19页,共63页,2024年2月25日,星期天6.4纯虚函数与抽象类 如果由抽象类所派生出的新类中对基类的所有纯虚函数都进行了定义,这个派生类就不是抽象类,可以被调用,成为可以用来定义对象的具体类(concreteclass)。 如果由抽象类所派生出的新类中对基类的所有纯虚函数都没有进行定义,这个派生类就仍然是抽象类。 虽然抽象类不能定义对象,但可以定义指向抽象类的数据的指针。当派生类成为具体类之后,就可以用这种指针向派生类对象,然后通过该指针调用虚函数,实现多态性操作。

第20页,共63页,2024年2月25日,星期天编程练习:

定义虚函数,基类为SHAPE,派生出圆形、矩形、三角形类。第21页,共63页,2024年2月25日,星期天#include<iostream>usingnamespacestd;//定义抽象基类ShapeclassShape{public://定义纯虚函数};//定义Circle类classCircle:publicShape{public:Circle(doubler):radius(r){}//定义虚函数

protected:doubleradius;//半径};//定义Rectangle类classRectangle:publicShape{public:Rectangle(doublew,doubleh):width(w),height(h){}//定义虚函数

protected:doublewidth,height;};classTriangle:publicShape{public:Triangle(doublew,doubleh):width(w),height(h){}//定义虚函数

protected:doublewidth,height;};第22页,共63页,2024年2月25日,星期天#include<iostream>usingnamespacestd;//定义抽象基类ShapeclassShape{public:virtualdoublearea()const=0;//纯虚函数};//定义Circle类classCircle:publicShape{public:Circle(doubler):radius(r){}

virtualdoublearea()const{return3.14159*radius*radius;};//定义虚函数

protected:doubleradius;//半径};//定义Rectangle类classRectangle:publicShape{public:Rectangle(doublew,doubleh):width(w),height(h){}

virtualdoublearea()const{returnwidth*height;}//定义虚函数

protected:doublewidth,height;};classTriangle:publicShape{public:Triangle(doublew,doubleh):width(w),height(h){}

virtualdoublearea()const{return0.5*width*height;}//定义虚函数

protected:doublewidth,height;};第23页,共63页,2024年2月25日,星期天续:定义一个函数printArea,以对象为形参,输出三种形状的面积

intmain(){Circlecircle(12.6);//建立Circle类对象circlecout<<"areaofcircle=";printArea(circle);//输出circle的面积

Rectanglerectangle(4.5,8.4);//建立Rectangle类对象rectanglecout<<"areaofrectangle=";printArea(rectangle);//输出rectangle的面积

Triangletriangle(4.5,8.4);//建立Triangle类对象

cout<<"areaoftriangle=";printArea(triangle);//输出triangle的面积

return0;}第24页,共63页,2024年2月25日,星期天//输出面积的函数voidprintArea(constShape&s){cout<<s.area()<<endl;}第25页,共63页,2024年2月25日,星期天6.4纯虚函数与抽象类 多态性把操作细节留给类的设计者(专业人员)去完成,而让编程人员(类的使用者)只需做一些宏观性的工作,告诉系统做什么,不必考虑怎么做,简化了应用程序的编码工作。 因此有人说,多态性是开启继承功能的钥匙。第26页,共63页,2024年2月25日,星期天Object-Oriented

Programming

inC++

第七章输入输出流第27页,共63页,2024年2月25日,星期天第一章C++的初步知识第二章类和对象第三章再论类和对象第四章运算符重载第五章继承与派生第六章多态性与虚函数第七章输入输出流第八章C++工具第28页,共63页,2024年2月25日,星期天7.1C++的输入与输出7.2标准输出流7.3标准出入流7.4文件操作与文件流7.5字符串流第29页,共63页,2024年2月25日,星期天7.1C++的输入和输出输入输出的含义:从操作系统角度看,每一个与主机相连的输入输出设备都被看做一个文件。终端键盘是输入文件,终端显示器是输出文件。磁盘或光盘也可以被看作是输入输出文件。程序的输入:指的是从输入文件将数据传送给程序;程序的输出:指的是从程序将数据输出给输出文件。C++的输入输出包括以下三个方面的内容:标准设备输入输出,从键盘输入。输出到显示器。简称标准I/O。以外存储器文件为对象的输入输出。指从磁盘文件中输入数据,将数据输出到磁盘文件中。简称文件I/O对内存中指定的空间进行输入输出。通常指定一个字符数组作为存储空间,它称为字符串输入输出,简称串I/O C++采取了不同的方法,实现这三种输入输出。第30页,共63页,2024年2月25日,星期天7.1C++的输入和输出C++输入输出流

C++的输入输出流是指由若干字节组成的字节序列。在内存中,系统为每一个数据流开辟一个缓冲区,用来存放流中的数据。当使用cout和插入符“<<”输出数据时,先将这些数据送到程序中的输出缓冲区保存,直到缓冲区满了或遇到endl,就将缓冲区中的全部数据送到显示器。在输入时,从键盘输入的数据先放在键盘缓冲区中,形成cin流,然后用提取运算符“>>”从输入缓冲区提取数据,送给程序中的相关变量。 总之,内存缓冲区中的数据就是流。第31页,共63页,2024年2月25日,星期天7.1C++的输入和输出流类与流对象:在C++中,输入输出流被定义成类,C++的I/O库中的类称为流类(streamclass)。用流类定义的对象称为流对象。

cout和cin并不是C++提供的语句,它们是iostream类的对象。正如C++没有提供赋值语句,只提供了赋值表达式(表达式后面加分号,形成语句)。 在iostream头文件中重载运算符:“<<”和“>>”在C++中是位移运算符,由于在iostream头文件中对它们进行了重载,使它们能用做标准输入输出运算符,所以,在用它们的程序中必须使用#include<iostream>语句把iostream类包含到程序中。 下面我们来看看I/O类库中类的情况。第32页,共63页,2024年2月25日,星期天7.1C++的输入和输出I/O类库中常用的流类

strstreamstrstreamstrstream输入字符串流类输出字符串流类输入输出字符串流类istrstreamostrstreamstrstreamfstreamfstreamfstream输入文件流类输出文件流类输入输出文件流类ifstreamofstreamfstreamiostreamiostreamiostream通用输入流和其他输入流的基类通用输出流和其他输出流的基类通用输入输出流和其他输入输出流的基类istreamostreamiostreamIostream抽象基类ios在哪个头文件中声明作用类名第33页,共63页,2024年2月25日,星期天7.1C++的输入和输出iostream文件中定义的4种流对象stderrostream_withassign屏幕标准错误流clogstderrostream_withassign屏幕标准错误流cerrstdoutostream_withassign屏幕标准输出流coutstdinistream_withassign键盘标准输入流cinC语言对应文件对应的类对应设备含义对象说明

cin是istream的派生类istream_withassign的对象,它是从键盘输入数据流到内存;

cout是ostream的派生类ostream_withassign的对象,它从内存输出数据流到显示器;

cerr和clog相似,均为向显示器输出出错信息第34页,共63页,2024年2月25日,星期天7.2标准输出流clog流对象:也是标准出错流,是consolelog的缩写。作用和cerr相同。区别在于,cerr不经过缓冲区,直接向显示器输出,而clog经过缓冲区,缓冲区装满后或遇到endl时,向显示器输出。#include<iostream.h>#include<math.h>voidmain(){floata,b,c,disc;cout<<"pleaseinputa,b,c:";cin>>a>>b>>c;if(a==0)

cerr<<"aisequaltozero,error!"<<endl;elseif((disc=b*b-4*a*c)<0)

cerr<<"disc=b*b-4*a*c<0"<<endl;else{cout<<"x1="<<(-b+sqrt(disc))/(2*a)<<endl;cout<<"x2="<<(-b-sqrt(disc))/(2*a)<<endl;}}第35页,共63页,2024年2月25日,星期天7.2标准输出流格式输出:输出数据时,为简便起见往往不指定输出格式。但也可以指定输出格式。 输出格式有两种方法:①使用控制符输出格式;②使用流对象的有关成员函数。使用控制符输出格式:这些控制符是在头文件iomanip中定义。程序必须包含该头文件。控制符作用dec,hex,oct分别设置整数的基数为10、16、8setbase(n)设置整数基数为n(n为10,16,8三者之一)setfill(c)设置填充字符c,c是字符常量或变量setprecision(n)设置实数精度为n位setw(n)设置字段宽度为nsetiosflags(ios::left/right)输出数据左/右对齐resetioflags()终止已设置的输出格式,在括号中指定内容第36页,共63页,2024年2月25日,星期天7.2标准输出流#include<iostream.h>#include<iomanip.h>intmain(){inta=123;char*pt=“China”;doublepi=22.0/7.0;cout<<“dec:”<<dec<<a<<endl;cout<<“hec:”<<hex<<a<<endl;cout<<“oct:”<<oct<<a<<endl;

cout<<setw(10)<<pt<<endl;cout<<setfill(‘*’)<<setw(10)<<pt<<endl;cout<<“pi=”<<pi<<endl;cout<<“pi=“<<setprecision(4)<<pi<<endl;cout<<“pi=“<<setiosflags(ios::fixed)<<pi<<endl;return0;}第37页,共63页,2024年2月25日,星期天7.2标准输出流用流成员函数put输出字符 我们已经知道,程序中一般用cout<<实现输出,cout流在内存中有相应的缓冲区。 有时,我们想只输出一个字符,ostream类提供了put成员函数来满足这一特殊需求:#include<iostream.h>intmain(){char*a="BASIC";for(inti=4;i>=0;i--)cout.put(*(a+i));cout.put('\n');return0;}第38页,共63页,2024年2月25日,星期天7.3标准输入流cin流:cin流是istream类的对象。它从标准输入设备(键盘)获取数据,程序中的变量通过流提取符“>>”从流中提取数据。用于字符输入的流成员函数:用get函数读入一个字符:get函数有三中形式:无参数、有一个参数和3个参数的形式。不带参数的get函数:其调用形式为:cin.get()#include<iostream.h>intmain(){charc;cout<<“inputasentence,please:"<<endl;while((c=cin.get())!=EOF)cout<<c;return0;}第39页,共63页,2024年2月25日,星期天7.3标准输入流有一个参数的get函数:调用形式为:cin.get(ch)。作用是从输入流中读取一个字符,并赋给变量ch。#include<iostream.h>intmain(){charc;cout<<"inputeasectence,please:"<<endl;while(cin.get(c)){cout.put(c);}cout<<"end"<<endl;return0;}第40页,共63页,2024年2月25日,星期天7.3标准输入流有三个参数的get函数:调用形式:

cin.get(字符数组/字符指针,字符个数n,终止字符) 其作用是,从输入流中读取n-1个字符,并赋给指定的数组(或指针指向的数组)。如果在读取中遇见终止字符,则提前结束读取操作。#include<iostream.h>intmain(){charch[20];cout<<"inputeasectence,please:"<<endl;cin.get(ch,18,'\n');cout<<ch<<endl;return0;}第41页,共63页,2024年2月25日,星期天7.3标准输入流用成员函数getline读入一行字符。调用形式:

getline(字符数组/字符指针,字符个数n,终止字符)

作用是从读入流中读取一行字符,并赋给字符数组。第42页,共63页,2024年2月25日,星期天7.3标准输入流istream类其它成员函数eof函数:表示文件结束。从输入流中读入文件,当达到文件尾,则eof函数值为非0(表示真),否则为0(假)。#include<iostream.h>intmain(){charc;while(!cin.eof())if((c=cin.get())!='')cout.put(c);elsecout.put('_');return0;}第43页,共63页,2024年2月25日,星期天7.4文件操作与文件流文件的概念:文件一般是指存储在外部介质上的数据集合。我们主要是指数据文件。根据数据的组织形式,数据文件分为:ASCII文件:又称为文本文件,它的每一个字节放一个ASCII代码,代表一个字符。二进制文件:又称为内部格式文件,它把数据按其在内存中的存储形式远洋输出到磁盘上存放。 对于字符信息,在内存中是以ASCII代码形式存放的,因此,无论用ASCII文件输出,还是用二进制文件输出,其数据形式是一样的。但是对于数值数据,二者是不同的。第44页,共63页,2024年2月25日,星期天7.4文件操作与文件流文件流类与文件流对象:文件流是以外存文件为输入输出对象的数据流。输出文件流是从内存流向外存文件的数据;输入文件流是从外存文件流向内存的数据。文件与文件流的区别:文件流不是文件,而只是以文件为输入输出对象的流。C++定义的文件类:

ifstream类,是istream类派生,用于从磁盘文件输入数据。

ofstream是ostream类派生,用于向磁盘文件的输出。

fstream是iostream类的派生,用于磁盘文件的输入输出。第45页,共63页,2024年2月25日,星期天7.4文件操作与文件流文件的打开与关闭打开磁盘文件:打开文件是指在文件读写之前做必要的准备工作。包括:为文件对象和指定的磁盘文件建立关联,以便使文件流流向指定的磁盘文件;指定文件的工作方式,如,是读文件还是写文件,是操作ASCII文件还是二进制文件。 打开文件有两种实现方式:调用文件流的成员函数open。一般形式为:

文件流对象.open(磁盘文件名,输入输出方式); 注意,磁盘文件名可以包括路径,省略路径则默认为当前目录。如:

ofstreamoutfile;

//定义ofstream类对象outfile

outfile.open(“fi.dat,ios::out);

//使文件流与f1.dat文件建立关联第46页,共63页,2024年2月25日,星期天7.4文件操作与文件流在定义文件流对象时指定参数:比如

ostreamoutfile(“f1.dat”,ios::out);一般常用这种形式来打开文件。第47页,共63页,2024年2月25日,星期天7.4文件操作与文件流文件的输入输出方式设置值:方式作用ios::in以输入方式打开文件ios::out以输出方式打开文件。若文件存在,则清除原内容ios::app以输出方式打开文件。写入的数据增加在文件末尾ios::ate打开已有文件,文件指针指向文件末尾ios::trunc打开文件,若文件存在,则删除原数据ios::binary以二进制方式打开文件ios::in|ios::out以读写方式打开文件ios::out|ios::binary以二进制方式打开输出文件ios::in|ios::binar以二进制方式打开输入文件第48页,共63页,2024年2月25日,星期天7.4文件操作与文件流关闭磁盘文件:即解除该磁盘文件与文件流的关联。关闭文件的成员函数:

outfile.close();

第49页,共63页,2024年2月25日,星期天7.5字符串流字符串流的概念:文件流是以外存文件为输入输出对象的数据流,而字符串流是将数据输出到内存中的字符数组,或从字符数组读入数据。字符串流也称内存流。字符串流缓冲区:字符串流也有缓冲区。一开始,缓冲区为空。如果向字符数组存入数据,随着向流插入数据,流缓冲区中的数据不断增加。当缓冲区满或遇到换行符,缓冲区内容变全部存入字符数组。如果是从字符数组读数据,先将字符数组中的数据送到缓冲区,然后从缓冲区提取数据赋给有关变量。第50页,共63页,2024年2月25日,星期天7.5字符串流 数据存入字符数组之前,先要将数据转换成ASCII代码,然后放入缓冲区,再从缓冲区送到字符数组。 字符串流类的类型有:Istrstream类;Ostrstream类;Strstream类。 文件流类和字符串流类都是ostream类、istream类和iostream类的派生类。

第51页,共63页,2024年2月25日,星期天7.5字符串流建立输出字符串流对象:ostrstream类提供的构造函数原型为:

ostrstream::ostrstream(char*buffer, intn, intmode=ios::out);其中:buffer是指向字符数组首元素的指针;n为指定的流缓冲区大小,一般与字符数组大小相同;第三个参数可选,默认为ios:;out方式,可以用以下语句建立输出字符串流对象并与字符数组建立关联:

ostrstreamstrout(ch1,20); 作用是建立输出字符串流对象strout与字符数组ch1关联,缓冲区大小为20。第52页,共63页,2024年2月25日,星期天7.5字符串流建立输入输出字符串流对象:strstream类提供的构造函数原型为:

strstream::strstream(char*buffer, intn, intmode);

可以用以下语句建立输入输出字符流对象:

strstreamstrio(ch3,sizeof(ch3),ios::in|ios::out);

作用是建立输入输出字符串流对象,以字符数组ch3为输入输出对象,流缓冲区大小与ch3相同。第53页,共63页,2024年2月25日,星期天7.5字符串流例:将三个学生数据保存在字符数组中。#include<iostream>#include<strstream>usingnamespacestd;structstudent{intnum;charname[10];intscore;};voidmain(){studentstud[3]={1001,"Li",78,1002,"Liu",98,1003,"Ge",90};charc[50];

ostrstreamstrout(c,30);for(inti=0;i<3;i++)

strout<<stud[i].num<<stud[i].name<<stud[i].score;strout<<ends;cout<<"arrayc:"<<c<<endl;}第54页,共63页,2024年2月25日,星期天Object-Oriented

Programming

inC++

第八章C++工具第55页,共63页,2024年2月25日,星期天8.1命名空间8.2使用早期的函数库第56页,共63页,2024年2月25日,星期天8.1命名空间 本课程的各章节的程序中,都用到了这样的语句:

usingnamespacstd;

这就是命名空间std。为什么需要命名空间?C语言定义了3个作用域,即文件域,函数域和复合语句域。C++又引入了类作用域。 不同的作用域中可以用相同的变量名,互不干扰。但是,如果是在不同的库文件(*.h)中,有相同的变量名和类名,而不巧又在被一个程序包含、主文件中又调用了该变量,定义了该类对象,于是引起矛盾冲突。第57页,共63页,2024年2月25日,星期天8.1命名空间什么是命名空间?为了解决这个问题,ANSIC++增加了命名空间的概念。简单地说,就是ANSIC++引入的,可以由用户命名的内存区域,把一些全局实体分别放在各个命名空间中,从而与其他全局实体分隔开来。比如:

namespacensl {inta; doubleb; }其中:namespace是定义命名空间的关键字;nsl是用户指定的空间名。花括号内包含的a,b,是命名空间成员。第58页,共63页,2024年2月25日,星期天8.1命名空间 注意a和b仍然是全局变量,仅仅把它们隐藏在命名空间中,而程序中如果要使用变量a和b,必须加上空间名和域分辨符。如:nsl::a,nsl::b等。这些名字称为被限定名。

C++中的命名空间和被限定名的关系,类似与操作系统中文件夹和其中文件的关系。命名空间的作用:是建立一些互相分隔的作用域,把一些全局实体分割开来,以免产生名字冲突。命名空间中的被限定名可以是:常量和变量(可以带有初始化);函数(可以是定义或声明);结构体或类;模板或另一个命名空间(意

温馨提示

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

评论

0/150

提交评论