C++高级编程五v1.2 暗影行者_第1页
C++高级编程五v1.2 暗影行者_第2页
C++高级编程五v1.2 暗影行者_第3页
C++高级编程五v1.2 暗影行者_第4页
C++高级编程五v1.2 暗影行者_第5页
已阅读5页,还剩120页未读 继续免费阅读

下载本文档

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

文档简介

1、1C C+高级编程高级编程 ( (五五) )联航精英训练营2C+C+的多态性的多态性3多态性多态性n 多态性(polymorphism)是面向对象程序设计的主要特征之一。多态性对于软件功能的扩展和软件重用都有重要的作用。n 多态性:发出同样的消息被不同类型的对象接收导致完全不同的行为;n 面向对象程序设计中多态性表现为以下几种形式:p重载多态:通过调用相同名字的函数,表现出不同的行为。运算符重载也是一种重载多态。p运行多态:通过基类的指针,调用不同派生类的同名函数,表现出不同的行为。p模板多态,也称为参数多态:通过一个模板,得到不同的函数或不同的类。这些函数或者类具有不同的特性和不同的行为。4

2、多态的实现多态的实现: :联编联编n 联编(binding)一个具有多态性的程序语句,在执行的时候,必须确定究竟是调用哪一个函数。也就是说,在执行的时候调用哪个函数是唯一的确定的,确定具有多态性的语句究竟调用哪个函数的过程称为联编(binding),有的资料也翻译成“绑定”。n 联编有两种方式:静态联编和动态联编p 静态联编:在程序编译时刻就能决定调用的是哪个函数,因此静态的多态性又称编译时的多态性 主要靠函数重载和操作符重载实现。p 动态的多态性(动态束定)在程序的运行过程中动态确定操作所指的对象,因此又称运行时的多态性。 主要靠虚函数实现。5例子例子-t1.cpp-t1.cpp#inclu

3、de using namespace std;class Pointpublic: Point(double i,double j)x=i; y=j; double area()const return 0;private: double x, y;class Rectangle:public Pointpublic: Rectangle(int i, int j, int k, int l); double area() const return w*h;private: double w, h;Rectangle:Rectangle(int i,int j,int k,int l):Poi

4、nt(i,j) w=k; h=l;void fun(Point &s) couts.area()endl;int main() Rectangle rect(3.0,5.2,15.0,25.0); fun(rect); return 0;输出:0结果不是我们想要的,如何处理?6虚函数虚函数n 虚函数是动态绑定的基础;p 说明方法: virtual() 在基类的函数的前面加上virtual关键字,那么所有的派生类都自动加上virtual关键字,故派生类的virtual关键字可以省略 优势:通过动态绑定可以使我们在编写程序时,使用继承层次中任意类型的对象,无须关心对象的具体类型,使用这些类

5、的程序无须区分函数是在基类还是在派生类中定义的。p 含义: 若类中一成员函数被说明为虚函数,则该成员函数在派生类中可能有不同的实现。当使用该成员函数操作指针或引用所标识的对象时,对该成员函数调用可采用动态绑定方式。7继承类图继承类图-t7.cpp-t7.cpp8虚函数例子虚函数例子#include using namespace std;class Pointpublic: Point(double i,double j)x=i;y=j; virtual double area()const return 0;private: double x,y;class Rectangle:public

6、 Pointpublic: Rectangle(int i,int j,int k,int l); virtual double area() const return w*h;private: double w,h;Rectangle:Rectangle(int i,int j,int k,int l):Point(i,j) w=k; h=l;/void fun(Point s) 结果将如何?void fun(Point &s) couts.area()endl;int main() Rectangle rect(3.0,5.2,15.0,25.0); fun(rect); retu

7、rn 0;输出:3759例子例子-t2.cpp-t2.cpp#include using namespace std;/虚函数的晚绑定只有对指针和引用有效class Basepublic: virtual int area() return 6; ;class Derived: public Basepublic: int area() return 12; ;int main() Base b; Derived d; coutb.area()endl; coutd.area()endl; b = d; coutb.area()endl; Base *b1 = new Derived; cou

8、tarea()endl; return 0;10动态束定实现的三个条件动态束定实现的三个条件n 要实现运行时的多态,需要以下条件:公共继承是基础;基类要有说明的是虚函数;调用虚函数操作的是指向对象的指针或者对象引用;或者是由成员函数调用虚函数;11虚函数有哪些限制虚函数有哪些限制? ?1. 如果虚函数在基类与派生类中出现,仅仅是名字相同,而形式参数不同,或者是返回类型不同,那么即使加上了virtual关键字,也是不会进行滞后联编的。2. 只有类的成员函数才能说明为虚函数,因为虚函数仅适合用于有继承关系的类对象,所以普通函数不能说明为虚函数。3. 静态成员函数不能是虚函数,因为静态成员函数的特点

9、是不受限制于某个对象。4. 内联(inline)函数不能是虚函数,因为内联函数不能再运行中动态确定位置。即使虚函数在类的内部定义,但是在编译的时候系统仍然将它看做是非内联的。5. 构造函数不能是虚函数,因为构造的时候,对象还是一片未定型的空间,只有构造完成后,对象才是具体类的实例。6. 构析函数可以是虚函数,而且通常声明为虚函数。12虚析构函数虚析构函数n 虚析构函数:在析构函数前加关键字virtual进行说明,则该析构函数称为虚析构函数;n 如果一个类的析构函数被说明为虚析构函数,则它的派生类中的析构函数也是虚析构函数,不管它是否使用了关键字virtual进行说明n 目的:p使用delete

10、运算符删除一个对象时,能保证析构函数被正确地执行;n 格式:class Bpublic: virtual B();;13纯虚函数纯虚函数n 引入p在基类中不能为虚函数给出一个有意义的实现时,可将其声明为纯虚函数,其实现留待派生类完成;n 作用p为派生类提供一个一致的接口n 声明格式class virtual ()=0; ; 14抽象类抽象类n 定义:含有纯虚函数的类称为抽象类;n 抽象类只能作为基类使用,其纯虚函数的实现由派生类给出;但派生类仍可不给出纯虚函数的定义,继续作为抽象类存在;n 抽象类是不能实例化的,不能生成抽象基类的对象,因此一般讲该类的构造函数说明为保护的访问控制权限。p只能作

11、为别的类的基类。15抽象类的作用抽象类的作用n 用作基类:在一个继承层次结构中,提供一个公共的根,并基于抽象类的操作,设计出对抽象类所描述的一类对象进行操作的公共接口,其完整的实现由派生类完成。n 用作指针或引用的基类型:保证进入继承层次的每个类都具有(提供)纯虚函数所要求的行为。n 在成员函数内可以调用纯虚函数,但在构造函数或析构函数内不能调用纯虚函数(纯虚函数没有实现代码)class Apublic : virtual void f() = 0; void g() f(); /正确 A() f(); /错误16总结总结n 对象的最基本特征是封装和继承n 作为一种抽象数据,对象把实体的属性(

12、相关特征)和操作封装在一起,允许人们用自然的方式去模拟外部实体的结构和行为。n 继承是对象类实现可重用性和可扩充性的关键特征。在继承关系下对象类之间组成网状的层次结构。n 继承也带来了对象变量运行时绑定的复杂性,多态性和动态绑定使对象的类型机制实现更加灵活方便。17重载与重写的概念与区别重载与重写的概念与区别n 函数重写(override)也称作覆盖:是用于类的继承中,函数名、参数个数、类型都相同,仅函数体不同。n 函数重载(overload):是指同一作用域的不同函数使用相同的函数名,但是函数的参数个数或类型不同,简单地讲,就是不同的函数使用同一标识符,并且这些函数位于同一作用域。n 函数隐

13、藏:既不是重载也不是重写,例如:函数名及参数完全相同却又不是虚函数,却在子类中重新实现该函数,也就是所谓的隐藏。18重写重写/覆盖覆盖n 覆盖是指派生类函数覆盖基类函数,特征是:(1)不同的范围(分别位于派生类与基类);(2)函数名字相同;(3)参数相同;(4)基类函数必须有virtual关键字。 (5)重写函数必须和原函数具有相同的返回类型。 (6)const可能会使虚成员函数的重写失效。19函数重载函数重载 成员函数被重载的特征:(1)相同的范围(在同一个类中);(2)函数名字相同;(3)参数不同;(4)virtual关键字可有可无。20函数隐藏函数隐藏n “隐藏”是指派生类的函数屏蔽了与

14、其同名的基类函数,规则如下:(1)如果派生类的函数与基类的函数同名,但是参数不同。 此时,不论有无virtual关键字,基类的函数将被隐藏(注意别与重载混淆)。(2)如果派生类的函数与基类的函数同名,并且参数也相同,但是基类函数没有virtual关键字。 此时,基类的函数被隐藏(注意别与覆盖混淆)。21实例实例研究:应用多态的工资发放系统研究:应用多态的工资发放系统n 某家公司按周支付雇员工资。n 雇员一共有4类:p定薪雇员:不管每周工作多长时间都领取固定的周薪;p钟点雇员:按工作的小时数领取工资,并且可以领取超过40小时以外的加班费;p佣金雇员:工资完全是销售业绩提成;p带薪佣金雇员:工资是

15、基本工资加销售业绩提成。在这次工资发放阶段,公司决定奖励带薪佣金雇员,把他们的基本工资提高10%。公司想实现一个C+程序多态的执行工资的计算。22练习练习n 构造异质链表23虚函数的实现机制虚函数的实现机制24效率的考虑效率的考虑n 面向对象语言需要动态绑定,即在运行时确定具体的操作调用。因此需要在运行时查找整个继承树,从而降低了效率。n 大多数语言优化了查找的机制,通常采用方法隐藏的技术,即把子类中的操作统一编码存贮在表中,从而使得绑定变成了查找hash表。在C+,这个表可以存放函数的指针,使效率得到了提高。25虚函数的实现机制虚函数的实现机制n 动态绑定又称滞后绑定,即在编译期并不确定函数

16、调用语句的执行代码,而是为它生成虚函数表用以存放同名,同参,同返回值的虚函数地址。真正的绑定推迟到程序运行时再完成。在运行中遇到以指针调用虚函数的语句时,现场决定应执行的具体代码。n 动态绑定是通过虚函数表实现的对于含有虚函数的多态,编译器为每个对象生成一个虚表指针,即在每个对象的内存映像中增加了一个_vfptr指针,它指向一个虚函数vtable。例如在基类的虚函数表中列出基类所有虚函数的入口地址。26虚函数的内存映像虚函数的内存映像27例子例子-t4.cpp-t4.cpp#include using namespace std;class Aprotected: int k;public:

17、A()k = 3; virtual int getValue()return k;class B:public A public: virtual int getValue()return k*2;void F(A *p) coutgetValue()endl;int main() A *ba = new A; B *bp = new B; coutsizeof(ba)endl; coutsizeof(*ba)endl; F(bp); return 0;输出结果应该是什么?28虚函数实现原理虚函数实现原理29异常处理异常处理30C C错误处理机制错误处理机制1. 函数返回错误编码2. 使用全局

18、变量保存错误编码3. 出错时终止程序运行n 缺点:1.难以统一标准2.复杂3. 构造和析构函数没有返回值4. 出错就返回,用户接受度低31异常处理机制异常处理机制n C+异常处理的基本思想是简化程序的错误代码,为程序的健壮性提供了一个标准检测机制:若底层发生问题,则逐级上报,直到有能力处理此异常为止。n 异常处理的优势:1. 增强程序的健壮性。2. 使代码变得更简洁优美,更易维护。3. 错误信息更灵活丰富。n 缺点:1. 性能下降5%14%。2. 破坏了程序的结构性。3. 完成正确的异常安全的代码,需要付出更大的代价。32抛出异常抛出异常任何时候,程序在执行中遇到了非正常状况都可以抛出异常异常

19、用throw语句抛出A *p = new A;if (p = NULL) throw string(“Out of Memory.”);一旦抛出异常,则程序将在throw语句处跳出33捕获异常捕获异常异常也由程序员负责捕获用trycatch( )语句来捕获异常没有捕获的异常将被忽略try catch(string &s) catch(double x) 34异常的传递异常的传递n 产生异常之后,程序会立刻跳转p 跳出到最近的一层捕获异常的语句p 如果当前没有捕获语句或者捕获语句中没有匹配的异常,则程序会跳出当前的函数n 在函数的调用处,如果没有捕获住异常,则直接跳转到更高一层的调用者n

20、 如果一直没有捕获该异常,+将会使用默认的异常处理函数p 该处理函数可能会让程序最终跳出main函数并导致程序异常终止35捕获任意捕获任意异常异常-t5.cpp-t5.cpp#include using namespace std;int main( ) int a, b; a = 8; b = 4; try if (b = 0) throw Divided by Zero!; else double c = a/b; cout c endl; catch(const char *) cout Exception caught! endl; 36标准标准IOIO库库37标准标准IOIO头文件头

21、文件类型类型iostreamistream 从流中读取ostream写到流中去iostream对流进行读写;从istream和ostream派生fstreamifstream从文件中读取,由istream派生而来ofstream写到文件中去;由ostream派生而来fstream读写文件;由iostream派生而来sstreamistringstream从string对象中读取;由istream派生而来ostringstream写到string对象中去;由ostream派生而来stringstrem对string对象进行读写;由iostream派生而来参考:C+ Primer中文版第8章标准I

22、O库38IOIO类型特性类型特性n IO类中对国际字符的支持 标准库中还定义了一组相关的类型,支持wchar_t类型. 在每个类都加上“w” 前缀,以此与char类型的版本区分开来IO对象不可复制和赋值 ofstream out1, out2; out1 = out2; /错误 形参或返回类型也不能为流类型。如果需要传递或返回IO对象,则必须传递或返回该对象的指针或引用。39流的概念及用途流的概念及用途n C+的I/O是以字节流的形式实现的,流(stream)实际上就是一个字节序列。n输入流:在输入操作中,字节从输入设备(如键盘、磁盘、网络连接等)流向内存;n输出流:在输出操作中,字节从内存流

23、向输出设备(如显示器、打印机、磁盘、网络连接等);n 应用程序把字节的含义与字节关联起来。n 输人/输出系统的任务实际上就是以一种稳定、可靠的方式在设备与内存之间传输数据。40有关流的头文件有关流的头文件n iostream:包含了操作所有输入/输出流所需的基本信息,因此大多数C+程序都必须包含它。n iomanip:如果用到了含参数的流操纵算子,则必须包含它,如需使用setfill, setw, setbase, setprecision等。n fstream:包含了文件操作所需的基本信息。当程序用到文件时,必须包含它。41输入输出流类输入输出流类iosistreamostreamifstr

24、eamofstreamiostreamfstream42标准流标准流n cin是类istream的对象,它与标准输入设备(通常指键盘)连在一起。n cout是类ostream的对象,它与标准输出设备(通常指显示设备)连在一起。n cerr是类osteam的对象,它与标准错误输出设备连在一起,对象cerr的输出是无缓冲的。 n clog是类ostream的对象,它与标准错误输出设备连在一起,对象clog的输出是有缓冲的。43流的概念及用途流的概念及用途n I/O操作是以对数据类型敏感的方式执行的。C+的I/O操作是以字节流的形式实现的。流实际上就是字节序列。n C+提供了低级和高级I/O功能。低

25、级I/O功能通常只在设备和内存之间传输一些字节。高级I/O功能把若干个字节组合成有意义的单位,如整数、浮点数、字符、字符串以及用户自定义类型的数据。n C+提供了无格式I/O和格式化I/O两种操作。无格式I/O传输速度快,但使用起来较为麻烦。格式化I/O按不同的类型对数据进行处理,但需要增加额外的处理时间,不适于处理大容量的数据传输。 44输出流输出流n C+的类ostream提供了格式化输出和无格式输出的功能 n 输出功能包括p用流插入运算符输出标准类型的数据;p用成员函数put输出字符;p成员函数write的无格式化输出;p输出特定形式数值45流插入运算符流插入运算符n 输出标准类型的数据

26、。n 格式: cout 数据项 ;n eg. #include using namespace std;int main() cout Welcome to C+!n; return 0;输出结果:Welcome to C+!流插入运算符46输出流输出流输出char*类型的变量#include using namespace std;int main()const char *string = test;cout Value of string is: string nValue of static_cast( string ) is: static_cast( string ) endl;r

27、eturn 0;输出结果:Value of string is: testValue of static_cast( string ) is:0 x80488a0返回char*类型的字符串 返回字符串中第一个字符的地址 输出的地址是用十六进制格式表示 47用成员用成员函数函数putput输出字符输出字符n cout.put(A); 将字符A显示在屏幕上。 连续调用put函数: cout.put(A).put(n); 该语句在输出字符A后输出一个换行符。圆点运算符(.)从左向右结合,put 成员函数返回调用put的对象的引用。n cout.put(65);用ASCII码值表达式调用put函数,语

28、句也输出字符A。48writewrite的无格式输出的无格式输出n 调用成员函数write可实现无格式输出。这个函数把一定量的字节从字符数组中输出。这些字节都是未经任何格式化的,仅仅是以原始数据形式输出。例如: char buffer = HAPPY BIRTHDAY; cout.write(buffer,10); 输出buffet的10个字节n 函数调用:cout.write(ABCDEFGHIJKLMNOPQRSTUVWXYZ,10); 显示了字母表中的前10个字母。 49输入流输入流n 流读取运算符 n get函数n getline函数n 其他函数50流读取运算符流读取运算符n 流的输入

29、最常用的是流读取运算符。n 流读取运算符通常会跳过输人流中的空格、tab键、换行符等空白字符。n 当遇到输入流中的文件结束符时,流读取运算符返回0(false);否则,流读取运算符返回对调用该运算符的对象的引用。51实例实例n 查找某次考试的最高成绩。假定事先不知道有多少个考试成绩,并且用户会输入表示成绩输入完毕的文件结束符。当用户输入文件结束符时,while循环结构中的条件(cingrade)将变为0(即false)。52#include using namespace std;int main() int grade, highestGrade = -1; cout grade) if (

30、 grade highestGrade )highestGrade = grade;cout Enter grade (enter end-of-file to end): ;cout nnHighest grade is: highestGrade endl; return 0;输出结果:Enter grade (enter end-of-file to end): 67Enter grade (enter end-of-file to end): 87Enter grade (enter end of file to end): 73Enter grade (enter end-of-fi

31、le to end): 95Enter grade (enter end-of-file to end): 34Enter grade (enter end-of-file to end): 99Entergrade (enter end-of-file to end): zHeighest grade is: 9953成员函数成员函数getgetn 不带参数的get函数从指定的输入流中读取(输入)一个字符(包括空白字符),并返回该字符作为函数调用的值;n 当遇到输入流中的文件结束符时,该版本的get函数返回EOF。 54#include using namespace std;int mai

32、n() char c; cout Before input, cin.eof() is cin.eof() nEnter a sentence followed by end-of-file:n; while (!cin.eof() c = cin.get(); cout.put( c ); cout nEOF in this system is: (int)c; cout nAfter input, cin.eof() is cin.eof() endl; return 0;55带一个字符型参数的带一个字符型参数的getget成员函数成员函数自动读取输人流中的下一个字符。例: char c1

33、,c2,c3; cin.get(c1); cin.get(c2); cin.get(c3);若输入:AB CD则:c1=A,c2=B,c3=n56 例子例子例:读取当前行的剩余字符,但不对它们作任何处理 void new_line( ) char symbol; do cin.get(symbol); while (symbol !=n); 57带有三个参数的带有三个参数的getget成员函数成员函数n 参数分别是接收字符的字符数组、字符数组的大小和分隔符(默认值为n)。n 函数或者在读取比指定的最大字符数少一个字符后结束,或者在遇到分隔符时结束。n 为使字符数组(被程序用作缓冲区)中的输入字

34、符串能够结束,函数不把分隔符放到字符数组中,但是分隔符仍然会保留在输入流中。因此第二次get函数调用所得的结果将是个空行,除非讲结束符从输入流中移除(可以使用cin.ignore())。58#include using namespace std;int main() const int SIZE = 80; char buffer1 SIZE ; char buffer2 SIZE ; cout Enter a sentence: buffer1; /cin.get(buffer1,SIZE, ); /cin.ignore(); cout nThe string read with cin

35、was: endl buffer1 endl endl; cin.get( buffer2, SIZE ); cout The string read with cin.get was: endl buffer2 endl; return 0;例子例子59成员函数成员函数getlinegetlinen 与带三个参数的get函数类似,它读取一行信息到字符数组中,然后插入一个空字符。n 所不同的是,getline要去除输入流中的分隔符(即读取字符并删除它),但是不把它存放在字符数组中。60#include using namespace std;int main() const int SIZE

36、= 80; char buffer SIZE ; cout Enter a sentence: endl; cin.getline( buffer, SIZE ); cout nThe sentence entered is: endl buffer endl; return 0;61istreamistream类中的其他成员函数类中的其他成员函数n ignore用于在需要时跳过流中指定数量的字符(默认个数是1),或在遇到指定的分隔符(默认分隔符是EOF,使得ignore在读文件的时候跳过文件末尾)时结束。n putback将最后一次用get从输人流中提取的字符放回到输入流中。n peek返回

37、输入流中的下一个字符,但并不将其从输入流中删除。 62istreamistream类中的其他成员函数类中的其他成员函数n 成员函数read无格式输入n gcount统计最后输入的字符个数63#include using namespace std;int main() const int SIZE = 80; char buffer SIZE ; cout Enter a sentence: endl; cin.read( buffer, 20 ); cout endl The sentence entered was: endl; cout.write( buffer, cin.gcount

38、() ); cout endl; return 0;例子例子输出结果:Enter a sentence:Using the read, write, and gcount member functionsThe sentence entered was:Using the read,write64格式化输入格式化输入/ /输出输出n C+提供了大量的用于执行格式化输入/输出的流操纵符和成员函数。n 功能: 整数流的基数:dec、oct、hex和setbase() 设置浮点数精度:precision、setprecision() 设置域宽:setw、width() 设置域填充字符:fill、se

39、tfill()65hexhex、octoct、decdec和和setbasesetbase#include #include using namespace std;int main() int number; cout number; cout number in hexadecimal is: hex number endl; cout dec number in octal is: oct number endl; cout setbase(10) number in decimal is: number endl; return 0;输出结果:Enter a decimalnumber

40、: 2020 in hexadecimal is:1420 in octal is: 2420 in decimal is: 20 66浮点精度(浮点精度(precisionprecision,setprecisionsetprecision)#include #include #include using namespace std;int main() double root2 = sqrt( 2.0 ); int places;/ use fixed point format cout fixed; for ( places = 0; places = 9; places+ ) cout

41、.precision( places ); cout root2 endl; for ( places = 0; places = 9; places+ ) cout setprecision( places ) root2 endl; return 0;67域域宽(宽(widthwidth,setwsetw)#include #include using namespace std;int main() int widthValue = 4; char sentence 10 ; cout Enter a sentence: sentence ) cout.width( widthValue

42、+ ); cout sentence endl; / coutsetw(widthValue+) sentence endl; cin.width( 5 ); return 0;68用户自定义的流操纵算子用户自定义的流操纵算子#include using namespace std;ostream& bell( ostream& output ) return output a; / issue system beep ostream& carriageReturn( ostream& output ) return output r; / issue carr

43、iage return ostream& tab( ostream& output ) return output t; / issue tab ostream& endLine( ostream& output ) return output n flush; / issue endl-like end of line 69输出结果:int main() cout Testing the tab manipulator: endLine a tab b tab c endLine; cout Testing the carriageReturn and bel

44、l manipulators: endLine .; cout bell; / use bell manipulator cout carriageReturn - endLine; return 0; 70小结小结 C+的I/O操作是以字节流的形式实现的。流实际上就是字节序列。输入/输出系统的任务实际上就是以一种稳定、可靠的方式在设备与内存之间传输数据。C+提供了低级和高级I/O功能。低级I/O功能通常只在设备和内存之间传输一些字节。高级I/O功能把若干个字节组合成有意义的单位。71小结小结C+提供了无格式I/O和格式化I/O两种操作。头文件iostream包括了操作所有输入/输出流所需的基

45、本信息,因此在大多数C+程序中都应该包含这个头文件。在执行格式化I/O时,如果流中带有含参数的流操纵算子,头文件iomanip所包含的信息是有用的。72小结小结头文件fstream包含了文件处理操作所需的信息。类istream支持流的输入操作。类ostream支持流的输出操作。类iostream同时支持流的输入、输出操做。类istream和ostream是通过单一继承从基类ios派生出来的。类iostream是通过多重继承从类istream和ostream派生出来的。73小结小结重载的左移位运算符()表示流的输入,称为流读取运算符。cin是类istream的对象,它与标准输入设备(通常指键盘)

46、“连”在一起。cout是类ostream的对象,它与标准输出设备(通常指显示器)“连”在一起。74小结小结流操纵算子endl用来插入一个换行符,并同时刷新输出缓冲区。C+编译器能够自动识别输入、输出数据的类型。我们通常在while循环结构的首部用流读取运算符输入一系列值。当遇到文件结束符时,流读取运算符返回0。75小结小结不带参数的get函数读取(输入)一个字符,并返回该字符;带一个字符型参数的get成员函数输入一个字符。带有三个参数的get成员函数,其三个参数分别是接收字符的字符数组、字符数组的大小和分隔符(默认值为n)。76文件文件77文件文件n 临时数据p存储在变量和数组中的数据是临时的

47、,这些数据在程序运行结束后都会消失。 n 文件p目的:文件用来永久地保存大量的数据。p存储:计算机把文件存储在二级存储设备中(特别是磁盘存储设备)。 78文件中的数据层次文件中的数据层次n 位(bit): 最小数据项:0和1n 字符(character)/字节(byte)字符:数字、字母和专门的符号字节:0、1序列 (常见的是8位/字节)字符用字节表示n 域(field):一组有意义的字符n 记录(record)p 一组相关的域p 记录关键字(record key):用于检索n 文件(file):一组相关的记录n 数据库:一组相关的文件79文件中记录的组织方式文件中记录的组织方式 n 文件中记

48、录常见的两种组织方式p顺序访问文件(sequential access file) : 按记录关键字字段的顺序存储记录的文件。 例:在工资表文件中记录通常按雇员标识号的顺序存储与访问。 p随机访问文件(random access file) 按随机顺序访问与存储记录的文件。80文件和流文件和流n C+语言把每一个文件都看成一个有序的字节流(把文件看成n个字节) p每一个文件或者以文件结束符(end-of-file marker)结束,或者在特定的字节号处结束p当打开一个文件时,该文件就和某个流关联起来p与这些对象相关联的流提供程序与特定文件或设备之间的通信通道 例如cin对象(标准输入流对象)

49、使程序能从键盘输入数据,cout对象(标准输出流对象)使程序能向屏幕输出数据,cerr和clog对象(标准错误流对象)使程序能向屏幕输出错误消息。 81ifstream, fstream, ofstreamifstream, fstream, ofstreamn 要在C+中进行文件处理,就要包括头文件和。p头文件包括流类ifstream(从文件输入)、ofstream(向文件输出)和fstream(从文件输入,输出)的定义。生成这些流类的对象即可打开文件。82建立顺序访问文件建立顺序访问文件 n C+把文件看作是无结构的字节流 n 记录的说法在C+文件中是不存在的。程序员必须提供满足特定应用程

50、序要求的文件结构。n 问题:怎样给文件强加一个记录结构 ? 83例子例子file_out.cppfile_out.cpp#include #include / file stream#include using namespace std;int main() ofstream outClientFile( clients.dat, ios:out ); / exit program if unable to create file if ( !outClientFile ) / overloaded ! operator cerr File could not be opened endl;

51、 exit( 1 ); cout Enter the account, name, and balance. endl account name balance ) outClientFile account name balance endl; cout account name balance )只要cin的failbit和badbit都没有设置,则条件保持true。输入文件结束符设置cin的failbit。operator void*函数可以测试输入对象的文件结束符,而不必对输入对象显式调用eof成员函数。90打开输入文件打开输入文件n ifstream inClientFile( “c

52、lients.dat”, ios:in ); p 生成ifstream对象inClientFile,并将其与打开以便输入的文件clients.dat相关联。括号中的参数传入ifstream构造函数,打开文件并建立与文件的通信线路。p 打开ifstream类对象默认为进行输入,因此下列语句等价: ifstream inClientFile( “Clients.dat” );p 和ofstream对象一样,ifstream对象也可以生成而不打开特定文件,然后再将用open对象与文件相连接。 91例子例子见代码file_in.cpp92文件位置指针文件位置指针n 为了按顺序检索文件中的数据,程序通常

53、要从文件的起始位置开始读取数据,然后连续地读取所有的数据,直到找到所需要的数据为止。可能需要反复多次。n 文件位置指针( file position pointer):用于指示读写操作所在的下一个字节号;是个整数值,指定文件中离文件开头的相对位置(也称为离文件开头的偏移量),93文件指针重新定位文件指针重新定位n istream类和ostream类都提供成员函数,使文件位置指针重新定位p istream类的seekg (即“seekget”) :每个istream对象有个get指针,表示文件中下一个输入相距的字节数p ostream类的seekp (即“seekput”):每个ostream对

54、象有一个put指针,表示文件中下一个输出相距的字节数n seekg和seekp的第一个参数通常为long类型的整数,表示偏移量。第二个参数可以指定寻找方向: ios:beg(默认)相对于流的开头定位 ios:cur相对于流当前位置定位 ios:end相对于流结尾定位94文件位置指针的例子文件位置指针的例子 / position to the nth byte of fileObject / assumes ios:beg fileObject.seekg( n ); / position n bytes forward in fileObject fileObject.seekg( n, io

55、s:cur ); / position y bytes back from end of fileObject fileObject.seekg( y, ios:end ); / position at end of fileObject fileObject.seekg( 0, ios:end );95获取文件指针值获取文件指针值n 成员函数tellg和tellp分别返回get和put指针的当前位置。下列语句将get文件位置指针值赋给long类型的变量location。 location = filObject.tellg();96更新顺序访问文件更新顺序访问文件n 格式化和写入顺序访问文件

56、的数据修改时会有破坏文件中其他数据的危险 n 例如,如果要把名字“White”改为“Worthington”,则不是简单地重定义旧的名字。White的记录是以如下形式写入文件中的: 300 White 0.00 如果用新的名字从文件中相同的起始位置重写该记录,记录的格式就成为: 300 Worthington 0.00因为新的记录长度大于原始记录的长度,所以从“Worthington”的第二个“o”之后的字符将重定义文件中的下一条顺序记录。n 出现该问题的原因在于:在使用流插入运算符的格式化输入,输出模型中,域的大小是不定的,因而记录的大小也是不定的。例如,7、14、-117、2047和273

57、83都是int类型的值,虽然它们的内部存储占用相同的字节数,因此,格式化输入输出模型通常不用来更新已有的记录。97更新顺序访问文件(续)更新顺序访问文件(续)n 一种解决方法:将在300 White 0.00之前的记录复制到一个新的文件中,然后写入新的记录并把300 White 0.00之后的记录复制到新文件中。这种方法要求在更新一条记录时处理文件中的每一条记录。如果文件中一次要更新许多记录,则可以用这种方法。98运算符重载运算符重载99运算符重载运算符重载n 运算符重载就是赋予已有的运算符多重含义。n C+中通过重新定义运算符,使它能够用于特定类的对象执行特定的功能,这便增强了C+语言的扩充

58、能力。100运算符重载的注意事项运算符重载的注意事项n 不能重载的运算符名 : .* . ? :n 重载运算符必须具有一个类类型(或枚举类型)操作数n 优先级和结合性是固定的 x = y + z;n 不再具有短路求值特性 重载运算符并不保证操作数的求值顺序, 尤其是逻辑运算和逗号操作符。重载&、|、或逗号 运算符不是好的做法。101类成员与非类成员类成员与非类成员n 大多数的重载运算符可以定义为普通非成员函数或类的成员函数。 作为类成员的重载函数,其形参看起来比操作数数目少1。 String& operator =(const String &rhs); String&

59、amp; String:operator = (const String &rhs)n 一般讲算术和关系操作符定义为非成员函数, 而将赋值操作符定义为成员;n 当操作符为成员函数时, this指针指向左操作数, 因此,非成员operator+定义两个形参。102操作符重载和友元的关系操作符重载和友元的关系n 操作符定义为非成员函数时, 通常必须将它们设置为所操作类的友元。因为在这种情况下,操作符通常需要访问类的私有部分。103重载运算符的使用重载运算符的使用1. 可以像使用内置类型操作符一样使用 cout a+b endl;2. 可以像调用普通函数一样调用重载运算符, 指定函数并传递适

60、当类型适当数目的参数。 cout operator+(a, b) endl;104重载运算符的设计重载运算符的设计n 不要重载具有内置含义的操作符 (& , & , |) 重载的赋值运算应当在赋值的内置含义基础上进行定制,而不是完全绕开。n 大多数操作符对类对象没有意义 根据需要将某些运算符重载n 复合赋值运算符 =, +=n 相等和关系运算符( = !=)n 选择成员或非成员实现 1. = () -等必须定义为成员(+=不是必须) 2. 改变对象状态或与给定类型密切联系的其他运算符,如自增、自减,应定义为类成员 3. 对称的操作符,如算术运算、关系操作符、位操作符。最好定义为普通非成员函数。警告: 不要滥用运算符重载105单目运算符的重载单目运算符的重载1 1n operator +作为友员的重载class A friend A& operator + (A&);int a,b;public:A(int x

温馨提示

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

评论

0/150

提交评论