38
第 12 第 第第第第第第第第第第第第第 12.1 运运运运运 12.2 第第第 12.3 第第 第第第第第第第第第第第第 ; 第第第第第第第第第 第第第第 C++ 第第第第第 ; 第第第第第第第第第第第第

第 12 章 面向对象程序设计的其它技术 12.1 运算符重载 12.2 流类库 12.3 模板 学习目的: ①掌握运算法的重载 ; ②了解流式文件的概念,并能进行

  • View
    278

  • Download
    0

Embed Size (px)

Citation preview

Page 1: 第 12 章 面向对象程序设计的其它技术 12.1 运算符重载 12.2 流类库 12.3 模板 学习目的: ①掌握运算法的重载 ; ②了解流式文件的概念,并能进行

第 12 章 面向对象程序设计的其它技术

12.1运算符重载12.2 流类库12.3 模板

学习目的:① 掌握运算法的重载 ;② 了解流式文件的概念,并能进行 C++ 文件的访问 ;③ 掌握模板的观念和使用方法。

Page 2: 第 12 章 面向对象程序设计的其它技术 12.1 运算符重载 12.2 流类库 12.3 模板 学习目的: ①掌握运算法的重载 ; ②了解流式文件的概念,并能进行

12.1 运算符重载

12.1.1 友元运算符12.1.2 类运算符12.1.3 友元及类运算符的应用12.1.4 ++ 和 -- 运算符的重载

C++ 通过以运算符函数形式定义运算符的操作,因此将运算符的重载问题转化为运算符函数的重载。

3@2 operator @(3 , 2) ,

<ReturnType> operator @ (<ArgList>){ /* 函数具体定义 */ }

Page 3: 第 12 章 面向对象程序设计的其它技术 12.1 运算符重载 12.2 流类库 12.3 模板 学习目的: ①掌握运算法的重载 ; ②了解流式文件的概念,并能进行

12.1.1 友元运算符

#include "iostream.h"class CComplex{ private: double m_dReal; double m_dImag; public: CComplex(double r=0, double i=0) { m_dReal=r; m_dImag=i; } double GetRealPart() { return m_dReal; } double GetImagPart() { return m_dImag; }};

CComplex operator + (CComplex& a, CComplex& b){ return CComplex( a.GetRealPart()

+b.GetRealPart(), a.GetImagPart()+b.GetImagPart() );}void main(){ CComplex c1(1, 2), c2(3, 4), z; z=c1+c2; cout<<z.GetRealPart()<<" + i"<<z.GetImagPart()<<endl;}

[ 例 12.1] 中 CComplex 类的定义中,将类的数据成员定义为公有成员,这种定义方式不是一个好的风格,不利于封装,一般应将类的数据成员定义为具有 private 属性,上述程序改写如下:

这样改动的结果势必造成程序运行效率的降低,因为调用函数需要额外的时间开销,可采用友元运算符,即可维持封装性,同时兼顾到运行效率。

Page 4: 第 12 章 面向对象程序设计的其它技术 12.1 运算符重载 12.2 流类库 12.3 模板 学习目的: ①掌握运算法的重载 ; ②了解流式文件的概念,并能进行

12.1.1 友元运算符

class CComplex{ private: double m_dReal; double m_dImag;

public: CComplex(double r=0, double i=0) { m_dReal=r; m_dImag=i; } double GetRealPart() { return m_dReal; } double GetImagPart() { return m_dImag; } friend CComplex operator + (CComplex& a, CComplex& b);};

CComplex operator + (CComplex& a, CComplex& b){ return CComplex(a.m_dReal+b.m_dReal, a.m_dImag+b.m_dImag); }

友元运算符定义方式如下:

<ReturnType> operator @ (<ArgList>){ /* 函数具体定义 */ }

friend <ReturnType> operator @ (<ArgList>);

void main(){ CComplex c1(1, 2), c2(3, 4), z; z=c1+c2; cout<<z.GetRealPart()<<" + i“ <<z.GetImagPart()<<endl;}

Page 5: 第 12 章 面向对象程序设计的其它技术 12.1 运算符重载 12.2 流类库 12.3 模板 学习目的: ①掌握运算法的重载 ; ②了解流式文件的概念,并能进行

12.1.2 类运算符运算符重载函数可以被说明为类成员函数,这种运算符称为类运算符 :

c1+c2 c1.operator(c2)

<ReturnType> operator @ (ClassName& obj)

[ 例 12.2] 定义运算符重载函数为成员函数#include "iostream.h"class CComplex{ private: double m_dReal; double m_dImag; public: CComplex(double r=0, double i=0) { m_dReal=r; m_dImag=i; } CComplex operator + (CComplex& a); void ShowValue() { cout<<m_dReal<<" + i"<<m_dImag<<endl; }};

CComplex CComplex::operator + (CComplex& a){ return CComplex(m_dReal+a.m_dReal, m_dImag+a.m_dImag); }

void main(){ CComplex c1(1, 2), c2(3, 4), z; z=c1+c2; z.ShowValue();}

Page 6: 第 12 章 面向对象程序设计的其它技术 12.1 运算符重载 12.2 流类库 12.3 模板 学习目的: ①掌握运算法的重载 ; ②了解流式文件的概念,并能进行

12.1.3 友元及类运算符的应用

对于 [ 例 12.2] 中运算符 + 的重载,如果主函数如下:void main(){

CComplex c(1, 2), z; z=3+c;}

有编译错误。因为程序中没定义友元运算符,编译器将 3+c 翻译成 3.operator +(c) ,报错。故此类情形只能定义友元函数。

如果需进行类运算符的多重运算,【例 12.2 】中 CComplex 类的对象 c1 、 c2 、 c3 进行 c1+c2+c3 ,因为 + 的结合性为从左至右,故编译器将之解释为 (c1.operator +(c2)).operator +(c3) ,因此要求运算符重载函数的返回值类型为 CComplex 或CComplex& 。

返回类型为对象时,将调用拷贝构造函数返回隐藏对象,其各数据成员的取值与被复制对象各数据成员取值间的关系取决于拷贝构造函数,而拷贝构造函数由编程者定义,具有不确定性,故建议返回引用类型,而且返回引用类型具有较高的运行效率。

与上述类似的原因,重载运算符的参数类型最好也为引用类型。

运算符重载方式具有下述规律:

Page 7: 第 12 章 面向对象程序设计的其它技术 12.1 运算符重载 12.2 流类库 12.3 模板 学习目的: ①掌握运算法的重载 ; ②了解流式文件的概念,并能进行

12.1.4 ++ 和 -- 运算符的重载因为存在前缀和后缀的情况,这两个运算符的重载有些复杂,用 @ 表示这两个运算符中的一个,它们的重载函数存在下述规律:

重载方式 表达式 C++编译器的解释

友元运算符 @obj operator@(obj)obj@ operator@(obj, 0)

类运算符 @obj obj.operator@()obj@ obj.operator@(0)

[ 例 12.4] 为 CPoint 类添加增 1 和减 1 类运算符(两个分量分别增、减 1 )#include "iostream.h"class CPoint{ private: double m_x; double m_y; public: CPoint(double x, double y) { m_x=x; m_y=y; } double GetX() { return m_x; } double GetY() { return m_y; }

Page 8: 第 12 章 面向对象程序设计的其它技术 12.1 运算符重载 12.2 流类库 12.3 模板 学习目的: ①掌握运算法的重载 ; ②了解流式文件的概念,并能进行

CPoint operator ++(int ) { // 后缀增 1 CPoint temp(*this); m_x++; m_y++; return temp; } CPoint operator ++( ) { m_x++; m_y++; return *this; } void ShowValue() { cout<<"("<<m_x<<", "<<m_y<<")"<<endl; }};void main(){ CPoint pt1(3, 5), pt2(6, 8); ++pt1; cout<<"pt1= "; pt1.ShowValue(); pt1=pt2++; cout<<"pt1= "; pt1.ShowValue(); cout<<"pt2= "; pt2.ShowValue();}

Page 9: 第 12 章 面向对象程序设计的其它技术 12.1 运算符重载 12.2 流类库 12.3 模板 学习目的: ①掌握运算法的重载 ; ②了解流式文件的概念,并能进行

12.2 流类库C++ 中的输入输出通过流完成,将数据对象输出是指将该对象的数据成员的值一个个地插入到输出流中;读入数据对象是指将输入流中的数据抽取出来,然后将它们转换成对象。即数据的输出和输入是一种数据的流动,从产生数据的源,流到接收数据的目标,源可以是对象、可以是文件,目标也可以是对象或者是文件。连接源和目标的是流,输入和输出都是相对流而言。

C++ 将有关流的属性及相应的输出输入操作封装在一起形成描述流的类(流类),将各种形式的流类汇聚在一处形成流的系统库—流类库。

streambuf

filebuf

strstreambuf

stdiobuf

ostream istream

iostream istrstream

ifstream

fstream strstreamstdiostream

ios

ostrstream

使用

ofstream

Page 10: 第 12 章 面向对象程序设计的其它技术 12.1 运算符重载 12.2 流类库 12.3 模板 学习目的: ①掌握运算法的重载 ; ②了解流式文件的概念,并能进行

12.2 流类库

12.2.1 格式化输入输出12.2.2 运算符 << 和 >> 的重载12.2.3 流式文件12.2.4 流错误

C++ 预定义四个流: cin 、 cout 、 cerror 和 clog ,这四个流与四个具体的输入输出设备相联接,具体的联接设备如下:

cin 与标准输入设备相联结 cout 与标准输出设备相联结 cerr 与标准错误输出设备相联结(非缓冲) clog 与标准错误输出设备相联结(缓冲)

Page 11: 第 12 章 面向对象程序设计的其它技术 12.1 运算符重载 12.2 流类库 12.3 模板 学习目的: ①掌握运算法的重载 ; ②了解流式文件的概念,并能进行

12.2.1 格式化输入输出通常使用标志位设置函数是 long setf(long)控制输入输出流格式, 该函数返回值为此次操作前的标志字。 ios 类含有多个与控制格式有关的函数。另外也可以使用内部格式控制操作函数 setiosflags(long)等控制输入输出流的格式,具体使用方法可参见 Visual C++ 联机帮助。 常量名 值 含 义 输入 /输出skipws 0x0001 跳过输入中的空白 ileft 0x0002 按输出域左边对齐输出 oright16 0x0004 按输出域右边对齐输出 ointernal 0x0008 在符号位或基指示符之后填充字符 odec 0x0010 以十进制形式输入输出 i/ooct 0x0020 以八进制形式输入输出 i/ohex 0x0040 以十六进制形式输出 i/oshowbase 0x0080 输出带有一个表示制式的字符(如 0x等) oshowpoint 0x0100 浮点数输出时必须带有一个小数点 ouppercase 0x0200 十六进制数字中的 X 和 A ~ F大写 oshowpos 0x0400 输出的正整数前带有 + 号 oscientific 0x0800 使用科学记数法表示浮点数 ofixed 0x1000 使用定点形式表示浮点数 ountibuf 0x2000 在输入操作后立即刷新流缓冲区 ostdio 0x4000 在输入操作后立即刷新 stdout 、 stderr o

Page 12: 第 12 章 面向对象程序设计的其它技术 12.1 运算符重载 12.2 流类库 12.3 模板 学习目的: ①掌握运算法的重载 ; ②了解流式文件的概念,并能进行

12.2.2 运算符 << 和 >> 的重载C++支持插入符 << 和抽取符 >> 的重载,函数重载可以直观地实现对任何数据对象进行输入输出控制,其格式如下:

ostream& operator <<(ostream& MyStream, <MyClassName> a){ // 抽取符重载函数 … return MyStream;}

istream& operator <<(istream& MyStream, <MyClassName> a){ // 插入符重载函数 … return MyStream;}

Page 13: 第 12 章 面向对象程序设计的其它技术 12.1 运算符重载 12.2 流类库 12.3 模板 学习目的: ①掌握运算法的重载 ; ②了解流式文件的概念,并能进行

[ 例 12.7] 重载复数类的插入符用于将复数打印在屏幕上#include "iostream.h"class CComplex{ private: double m_dReal; double m_dImag; public: CComplex(double r=0, double i=0) { m_dReal=r; m_dImag=i; } CComplex& operator + (const CComplex& a) { m_dReal+=a.m_dReal; m_dImag+=a.m_dImag; return *this; } friend ostream& operator <<(ostream& MyStream, const CComplex& a);};

ostream& operator <<(ostream& MyStream, const CComplex& a){ cout<<a.m_dReal<<" + i"<<a.m_dImag<<endl; return MyStream;}

void main(){ CComplex c1(1, 2), c2(3, 4), z; z=c1+c2; cout<<c1<<c2<<z;}

Page 14: 第 12 章 面向对象程序设计的其它技术 12.1 运算符重载 12.2 流类库 12.3 模板 学习目的: ①掌握运算法的重载 ; ②了解流式文件的概念,并能进行

12.2.3 流式文件C++支持流式文件,与文件的交互都被等效成对与文件相关联的数据流的插入和抽取,由流提供对文件的读写功能,至于流与文件之间如何交互由流去控制,编程者只需要和流打交道,而没有必要去关心文件的具体结构等低层次信息。 文件及其存取方式:顺序方式 以连续块的形式读写文本文件,即,按数据在文件中的物理排列顺序依次读出或写入。提供该方式的目的在于无格式文本文件中数据的存取,这种文件中数据被存储为 ASCII字符。随机方式 用于访问由定长记录组成的二进制及文本文件,通过文件指针的前后移动,可以即时读出文件中任意一处所存储的数据信息。文件被假定为由一组相同长度的记录组成,于是文件中的数据将按照记录的顺序加以存放,没有必要知道文件所存储的具体信息是什么,只需知道一个记录的宽度,使用一个指针通过简单的移动运算,标记目前所读记录的开始位置。二进制方式 用于访问任意结构的文件。可以使用二进制文件存储任何数据信息,二进制访问与随机访问类似,但对数据类型和结构的长度没做限定。显然,为了读二进制文件,必须精确地知道数据是如何写入文件的。

Page 15: 第 12 章 面向对象程序设计的其它技术 12.1 运算符重载 12.2 流类库 12.3 模板 学习目的: ①掌握运算法的重载 ; ②了解流式文件的概念,并能进行

12.2.3 流式文件1. 说明流与打开流文件对磁盘文件的访问,说明流和打开与流相关联的磁盘文件这两个操作可以同时完成,也可分步完成,说明流的目的是建立流,该流中保存有访问磁盘文件所需的所有信息和函数,打开流文件的过程是将流与某个具体的磁盘文件联系起来,从而进行该磁盘文件访问,方法如下:

fstream MyOutfile;MyOutfile.open("MyFile.txt", ios::out); // 以写方式打开文件或fstream MyOutfile("MyFile.txt", ios::out);或ofstream MyOutfile("MyFile.txt");

ios::in 以读方式打开文件ios::out 以写方式打开文件ios::ate 打开文件,将文件指针指向文件结尾ios::app 以将数据写到文件结尾(追加)的方式打开文件ios::trunc 如文件存在,清除原有内容,设文件长度为 0 。如文件不存在,创建新文件ios::binary 以二进制方式打开文件,缺省时为文本文件ios::nocreat

e打开已有文件,如文件不存在,打开失败

ios::noreplace

如文件存在,除非设置 ios::ate 或 ios::app,否则打开失败

Page 16: 第 12 章 面向对象程序设计的其它技术 12.1 运算符重载 12.2 流类库 12.3 模板 学习目的: ①掌握运算法的重载 ; ②了解流式文件的概念,并能进行

12.2.3 流式文件2. 关闭流及流文件系统同时能够打开的文件数有限,因此在文件读写操作完成后应及时关闭文件,将资源让出留给其它程序使用。关闭文件的方法很简单,对通过流类对象打开的文件可通过相应的对象调用函数 close( ) 即可。关闭流后,文件缓冲区中的数据被全部写入磁盘文件中。

ofstream file1("file1.dat");…file1.close();

Page 17: 第 12 章 面向对象程序设计的其它技术 12.1 运算符重载 12.2 流类库 12.3 模板 学习目的: ①掌握运算法的重载 ; ②了解流式文件的概念,并能进行

[ 例 12.8] 文本文件的读写#include "iostream.h"#include "fstream.h"void main(){ fstream MyFile1, MyFile2, MyFile3; char* pFileName="C:\\MyFile.txt"; MyFile1.open(pFileName, ios::nocreate); if(!MyFile1) { cout<<pFileName<<" can not be open!"<<endl; } MyFile1.close(); MyFile2.open(pFileName, ios::out); if(!MyFile2) { cout<<pFileName<<" can not be open!"<<endl; } MyFile2<<"The file "<<"\""<<pFileName; MyFile2<<"\" can not be open for the first time!"<<endl; MyFile2.close(); char CharArray[256]; MyFile3.open(pFileName, ios::in); if(!MyFile3) { cout<<pFileName<<" can not be open!"<<endl; } MyFile3.getline(CharArray, 256); cout<<CharArray<<endl; MyFile3.close();}

3. 文本文件的读写

Page 18: 第 12 章 面向对象程序设计的其它技术 12.1 运算符重载 12.2 流类库 12.3 模板 学习目的: ①掌握运算法的重载 ; ②了解流式文件的概念,并能进行

12.2.3 流式文件4 二进制文件的读写打开二进制文件需在方式控制中要使用 ios::binary 。访问二进制文件通常使用 read() 和 write() 两个成员函数,这两个函数有多种重载方式,其中最常用的方式为 :

istream& read( char* pch, int nCount );ostream& write( const char* pch, int nCount );

[ 例 12.10] 二进制文件的访问方法示例。下面程序中定义了描述学生的类 CStudent ,其中含有描述学生姓名(字符串)、学生年龄(整数)、学生生日( CDate 类型)、以及学生家庭住址( CAddress 类)。为了能够使用 << >> 等运算符与基本数据存储相统一的风格存取学生信息,在 CDate 、 CAddress 、 CStudent等类中对这两个运算符均进行了重载。

Page 19: 第 12 章 面向对象程序设计的其它技术 12.1 运算符重载 12.2 流类库 12.3 模板 学习目的: ①掌握运算法的重载 ; ②了解流式文件的概念,并能进行

#include "iostream.h"#include "fstream.h"#include "string.h"#include "iostream.h"#include "stdlib.h"class CDate{ private: int m_nDate; //日期 int m_nMonth; //月份 int m_nYear; //年 public: CDate(int nYear=0, int nMonth=0, int nDate=0) { m_nDate = nDate; m_nMonth = nMonth; m_nYear = nYear; } int GetDate() { return m_nDate; // 返回日期 } int GetMonth() { return m_nMonth; // 返回月份 } int GetYear() { return m_nYear; // 返回年 } friend ostream& operator<<(ostream& s, CDate& d); // 输出 CDate 对象 friend ofstream& operator<<(ofstream& s, CDate& d); // 输出 CDate 对象 friend ifstream& operator>>(ifstream& s, CDate& d); // 输入 CDate 对象};

Page 20: 第 12 章 面向对象程序设计的其它技术 12.1 运算符重载 12.2 流类库 12.3 模板 学习目的: ①掌握运算法的重载 ; ②了解流式文件的概念,并能进行

ostream& operator<<(ostream& s, CDate& d){ //按年、月、日顺序输出 s<<'('<<d.m_nYear<<','<<d.m_nMonth<<','<<d.m_nDate<<')'; return s;}ofstream& operator<<(ofstream& s, CDate& d){ s.write((char*)&d.m_nYear, sizeof(d.m_nYear)); s.write((char*)&d.m_nMonth, sizeof(d.m_nMonth)); s.write((char*)&d.m_nDate, sizeof(d.m_nDate)); return s;} ifstream& operator>>(ifstream& s, CDate& d){ s.read((char*)&d.m_nYear, sizeof(d.m_nYear)); s.read((char*)&d.m_nMonth, sizeof(d.m_nMonth)); s.read((char*)&d.m_nDate, sizeof(d.m_nDate)); return s;}class CAddress{ private: char* m_pczProvince; //省份名 char* m_pczCity; //城市名 int m_nMailBox; //邮箱编号 public: ~CAddress() { if(m_pczProvince!=0) delete[ ] m_pczProvince; if(m_pczCity!=0) delete[ ] m_pczCity; } CAddress(char* pczProvince="?",char* pczCity="?",int nMailBox=0) { m_pczProvince=new char[strlen(pczProvince)+1]; if(m_pczProvince!=0) strcpy(m_pczProvince, pczProvince); m_pczCity=new char[strlen(pczCity)+1]; if(m_pczCity!=0) strcpy(m_pczCity, pczCity); m_nMailBox=nMailBox; } friend ostream& operator<<(ostream& s, CAddress& d); friend ofstream& operator<<(ofstream& s, CAddress& d); friend ifstream& operator>>(ifstream& s, CAddress& d);};ostream& operator<<(ostream& s, CAddress& d){ s<<'('<<d.m_pczProvince<<','<<d.m_pczCity<<','<<d.m_nMailBox<<')'; return s;}ofstream& operator<<(ofstream& s, CAddress& d){ s.write(d.m_pczProvince, strlen(d.m_pczProvince)+1); s.write(d.m_pczCity, strlen(d.m_pczCity)+1); s.write((char*)&d.m_nMailBox, sizeof(d.m_nMailBox)); return s;}ifstream& operator>>(ifstream& s, CAddress& d){ char str[256]; s.get(str, 255, '\0'); delete d.m_pczProvince; d.m_pczProvince=new char[strlen(str)+1]; strcpy(d.m_pczProvince, str); s.get(); s.get(str, 255, '\0'); delete d.m_pczCity; d.m_pczCity=new char[strlen(str)+1]; strcpy(d.m_pczCity, str); s.get(); s.read((char*)&d.m_nMailBox, 4); return s;}enum SEX { male, female, unknow };class CStudent{ private: char* m_pczName; // 学生姓名 int m_nAge; // 学生年龄 SEX m_sSex; // 学生性别 CDate m_Birthday; // 学生生日 CAddress m_Address; // 学生家庭住址 public: CStudent( char* pName, int nAge, SEX s, int nYear=0, int nMonth=0, int nDate=0, char* pczProvince="?", char* pczCity="?", int nMailBox=0); CStudent(char* pName); ~CStudent(); friend ostream& operator<<(ostream& s, CStudent& d); friend ofstream& operator<<(ofstream& s, CStudent& d); friend ifstream& operator>>(ifstream& s, CStudent& d);};ostream& operator<<(ostream& s, CStudent& d){ s.put('(')<<d.m_pczName<<','; switch(d.m_sSex) { case male: s<<"male"; break; case female: s<<"female"; break; case unknow: s<<'?'; default: s<<"error"<<endl; } s<<','<<d.m_nAge<<','<<d.m_Birthday<<','<<d.m_Address<<')'; return s;}ofstream& operator<<(ofstream& s, CStudent& d){ s.write(d.m_pczName, strlen(d.m_pczName)+1); s.write((char*)&d.m_sSex, sizeof(d.m_sSex)); s.write((char*)&d.m_nAge, sizeof(d.m_nAge)); s<<d.m_Birthday<<d.m_Address; return s;}ifstream& operator>>(ifstream& s, CStudent& d){ char str[256]; s.get(str, 255, '\0'); delete d.m_pczName; d.m_pczName=new char[strlen(str)+1]; strcpy(d.m_pczName, str); s.get(); int n; s.read((char*)&n, sizeof(n)); switch (n) { case 0: d.m_sSex=male; break; case 1: d.m_sSex=female; break; case 2: d.m_sSex=unknow; break; default: cout<<"Error!"; abort(); } s.read((char*)&d.m_nAge, sizeof(d.m_nAge)); s>>d.m_Birthday>>d.m_Address; return s;}CStudent::CStudent(char* pName){ m_pczName=new char[strlen(pName)+1]; if(m_pczName!=0) strcpy(m_pczName, pName); m_nAge=-1; //-1 表示尚未初始化该属性 m_sSex=unknow;}CStudent::CStudent( char* pName, int nAge, SEX s, int nY, int nM, int nD, char* pProv, char* pCity,int nMBox) : m_Birthday(nY, nM,nD), m_Address(pProv, pCity, nMBox){ m_pczName=new char[strlen(pName)+1]; if(m_pczName!=0) strcpy(m_pczName, pName); m_nAge=nAge; m_sSex=s;}CStudent::~CStudent() { delete[ ] m_pczName; }void main(){ CStudent s1(""); CStudent *ps=new CStudent("李丽 ",10,female,1991,12,20,"河南 ","洛阳 ",180); ofstream MyFile("c:\\MyFile.txt", ios::binary); MyFile<<*ps; MyFile.close(); delete ps; ifstream MyFile1("c:\\MyFile.txt", ios::binary); MyFile1>>s1; cout<<s1<<endl; MyFile1.close();}

Page 21: 第 12 章 面向对象程序设计的其它技术 12.1 运算符重载 12.2 流类库 12.3 模板 学习目的: ①掌握运算法的重载 ; ②了解流式文件的概念,并能进行

ostream& operator<<(ostream& s, CDate& d){ //按年、月、日顺序输出 s<<'('<<d.m_nYear<<','<<d.m_nMonth<<','<<d.m_nDate<<')'; return s;}ofstream& operator<<(ofstream& s, CDate& d){ s.write((char*)&d.m_nYear, sizeof(d.m_nYear)); s.write((char*)&d.m_nMonth, sizeof(d.m_nMonth)); s.write((char*)&d.m_nDate, sizeof(d.m_nDate)); return s;} ifstream& operator>>(ifstream& s, CDate& d){ s.read((char*)&d.m_nYear, sizeof(d.m_nYear)); s.read((char*)&d.m_nMonth, sizeof(d.m_nMonth)); s.read((char*)&d.m_nDate, sizeof(d.m_nDate)); return s;}class CAddress{ private: char* m_pczProvince; //省份名 char* m_pczCity; //城市名 int m_nMailBox; //邮箱编号

Page 22: 第 12 章 面向对象程序设计的其它技术 12.1 运算符重载 12.2 流类库 12.3 模板 学习目的: ①掌握运算法的重载 ; ②了解流式文件的概念,并能进行

public: ~CAddress() { if(m_pczProvince!=0) delete[ ] m_pczProvince; if(m_pczCity!=0) delete[ ] m_pczCity; } CAddress(char* pczProvince="?",char* pczCity="?",int nMailBox=0) { m_pczProvince=new char[strlen(pczProvince)+1]; if(m_pczProvince!=0) strcpy(m_pczProvince, pczProvince); m_pczCity=new char[strlen(pczCity)+1]; if(m_pczCity!=0) strcpy(m_pczCity, pczCity); m_nMailBox=nMailBox; } friend ostream& operator<<(ostream& s, CAddress& d); friend ofstream& operator<<(ofstream& s, CAddress& d); friend ifstream& operator>>(ifstream& s, CAddress& d);};ostream& operator<<(ostream& s, CAddress& d){ s<<'('<<d.m_pczProvince<<','<<d.m_pczCity<<','<<d.m_nMailBox<<')'; return s;}

Page 23: 第 12 章 面向对象程序设计的其它技术 12.1 运算符重载 12.2 流类库 12.3 模板 学习目的: ①掌握运算法的重载 ; ②了解流式文件的概念,并能进行

ofstream& operator<<(ofstream& s, CAddress& d){ s.write(d.m_pczProvince, strlen(d.m_pczProvince)+1); s.write(d.m_pczCity, strlen(d.m_pczCity)+1); s.write((char*)&d.m_nMailBox, sizeof(d.m_nMailBox)); return s;}ifstream& operator>>(ifstream& s, CAddress& d){ char str[256]; s.get(str, 255, '\0'); delete d.m_pczProvince; d.m_pczProvince=new char[strlen(str)+1]; strcpy(d.m_pczProvince, str); s.get(); s.get(str, 255, '\0'); delete d.m_pczCity; d.m_pczCity=new char[strlen(str)+1]; strcpy(d.m_pczCity, str); s.get(); s.read((char*)&d.m_nMailBox, 4); return s;}enum SEX { male, female, unknow };

Page 24: 第 12 章 面向对象程序设计的其它技术 12.1 运算符重载 12.2 流类库 12.3 模板 学习目的: ①掌握运算法的重载 ; ②了解流式文件的概念,并能进行

class CStudent{ private: char* m_pczName; // 学生姓名 int m_nAge; // 学生年龄 SEX m_sSex; // 学生性别 CDate m_Birthday; // 学生生日 CAddress m_Address; // 学生家庭住址 public: CStudent( char* pName, int nAge, SEX s, int nYear=0, int nMonth=0, int nDate=0, char* pczProvince="?", char* pczCity="?", int nMailBox=0); CStudent(char* pName); ~CStudent(); friend ostream& operator<<(ostream& s, CStudent& d); friend ofstream& operator<<(ofstream& s, CStudent& d); friend ifstream& operator>>(ifstream& s, CStudent& d);};ostream& operator<<(ostream& s, CStudent& d){ s.put('(')<<d.m_pczName<<','; switch(d.m_sSex) { case male: s<<"male"; break; case female: s<<"female"; break; case unknow: s<<'?'; default: s<<"error"<<endl; } s<<','<<d.m_nAge<<','<<d.m_Birthday<<','<<d.m_Address<<')'; return s;}

Page 25: 第 12 章 面向对象程序设计的其它技术 12.1 运算符重载 12.2 流类库 12.3 模板 学习目的: ①掌握运算法的重载 ; ②了解流式文件的概念,并能进行

ofstream& operator<<(ofstream& s, CStudent& d){ s.write(d.m_pczName, strlen(d.m_pczName)+1); s.write((char*)&d.m_sSex, sizeof(d.m_sSex)); s.write((char*)&d.m_nAge, sizeof(d.m_nAge)); s<<d.m_Birthday<<d.m_Address; return s;}ifstream& operator>>(ifstream& s, CStudent& d){ char str[256]; s.get(str, 255, '\0'); delete d.m_pczName; d.m_pczName=new char[strlen(str)+1]; strcpy(d.m_pczName, str); s.get(); int n; s.read((char*)&n, sizeof(n)); switch (n) { case 0: d.m_sSex=male; break; case 1: d.m_sSex=female; break; case 2: d.m_sSex=unknow; break; default: cout<<"Error!"; abort(); } s.read((char*)&d.m_nAge, sizeof(d.m_nAge)); s>>d.m_Birthday>>d.m_Address; return s;}

Page 26: 第 12 章 面向对象程序设计的其它技术 12.1 运算符重载 12.2 流类库 12.3 模板 学习目的: ①掌握运算法的重载 ; ②了解流式文件的概念,并能进行

CStudent::CStudent(char* pName){ m_pczName=new char[strlen(pName)+1]; if(m_pczName!=0) strcpy(m_pczName, pName); m_nAge=-1; //-1 表示尚未初始化该属性 m_sSex=unknow;}CStudent::CStudent( char* pName, int nAge, SEX s, int nY, int nM, int nD, char* pProv, char* pCity,int nMBox) : m_Birthday(nY, nM,nD), m_Address(pProv, pCity, nMBox){ m_pczName=new char[strlen(pName)+1]; if(m_pczName!=0) strcpy(m_pczName, pName); m_nAge=nAge; m_sSex=s;}CStudent::~CStudent() { delete[ ] m_pczName; }void main(){ CStudent s1(""); CStudent *ps=new CStudent("李丽 ",10,female,1991,12,20,"河南 ","洛阳 ",180); ofstream MyFile("c:\\MyFile.txt", ios::binary); MyFile<<*ps; MyFile.close(); delete ps; ifstream MyFile1("c:\\MyFile.txt", ios::binary); MyFile1>>s1; cout<<s1<<endl; MyFile1.close();}

Page 27: 第 12 章 面向对象程序设计的其它技术 12.1 运算符重载 12.2 流类库 12.3 模板 学习目的: ①掌握运算法的重载 ; ②了解流式文件的概念,并能进行

12.2.3 流式文件5. 随机文件的读写对随机文件可以通过移动文件指针,将文件的读写定位在文件中任何结构的开始位置数据上,因此对文件随机读写操作的核心在于如何移动文件指针。C++ 中流的读写位置由文件指针标记,文件指针表示从文件开始位置到当前位置的字节数。以只读方式打开的流用读文件指针记录流的读位置,只写方式打开的流通过写文件指针记录流的当前写位置。以读写方式打开的流则同时利用这两个指针。以只读方式打开的流,抽取操作从当前文件指针所指位置开始,这个位置的改变由 istream 流类提供的移动文件指针函数完成。

istream& seekg( streampos pos );istream& seekg( streamoff off, ios::seek_dir dir );streampos tellg();ostream& seekp( streampos pos );ostream& seekp( streamoff off, ios::seek_dir dir );streampos tellp();

Page 28: 第 12 章 面向对象程序设计的其它技术 12.1 运算符重载 12.2 流类库 12.3 模板 学习目的: ①掌握运算法的重载 ; ②了解流式文件的概念,并能进行

12.2.4 流错误C++ 具有简单的错误检测机制,在文件操作中出现的错误由 ios 类的一个称为状态字的数据成员所记载,状态字的每一位代表不同的错误状态,这些位的值由下述常量描述:

ios::goodbit = 0x00 状态正常ios::eofbit = 0x01 到达文件结尾ios::failbit = 0x02 I/O 操作失败ios::badbit = 0x04 试图进行非法操作ios::hardbit = 0x80 致命错误

如果 failbit位置位表示流没有受到破坏,可以恢复。如果 hardbit位置位表明出现硬件设备错误,流受到不可恢复破坏。除 hardbit位外,上述错误位都可以使用上述常量通过调用函数 clear(int nState=0)清除。 ios 类提供了检测流状态的成员函数:

int rdstate() const; // 返回当前状态字int eof() const; // 返回非零值表示抽取操作到文件结尾int fail() const; // 返回非零值表示 feilbit位置位int bad() const; // 返回非零值表示 badbit位置位int good() const; // 返回非零值表示没有被置位的位

Page 29: 第 12 章 面向对象程序设计的其它技术 12.1 运算符重载 12.2 流类库 12.3 模板 学习目的: ①掌握运算法的重载 ; ②了解流式文件的概念,并能进行

12.3 模板

12.3.1函数模板12.3.2类模板12.3.3模板应用实例

C++ 的模板机制允许将类型作为定义类或函数的参数,使用不同的参数,利用模板,编程者可以使编译器创建出不同的类或函数,这些不同的函数和类之间的差别仅仅在于函数或类中所用到的一些数据对象的类型不同。

Page 30: 第 12 章 面向对象程序设计的其它技术 12.1 运算符重载 12.2 流类库 12.3 模板 学习目的: ①掌握运算法的重载 ; ②了解流式文件的概念,并能进行

12.3.1 函数模板函数模板的定义格式如下:

template < TYPE_LIST, ARG_LIST > Function_Definition

[ 例 12.12] 定义求两个数据中最大值的函数模板。#include "iostream.h"template <class TYPE>TYPE GetMax(TYPE x, TYPE y){ return x>y ? x : y; }void main(){ int n1(4), n2(8); double d1(2.1), d2(2.3); cout<<GetMax(n1, n2)<<endl; cout<<GetMax(d1, d2)<<endl; cout<<GetMax<int>(d1, d2)<<endl;}

定义了函数模板后,使用函数模板可以创建不同的函数实例,对于特殊情况通常定义一个特定参数类型的模板函数来解决,这个函数称为特定模板函数。例如可在上例中添加:

char* max(char* c1, char* c2){ return (strcom(c1, c2)>0) ? c1 : c2; }

Page 31: 第 12 章 面向对象程序设计的其它技术 12.1 运算符重载 12.2 流类库 12.3 模板 学习目的: ①掌握运算法的重载 ; ②了解流式文件的概念,并能进行

12.3.2 类模板C++提供的类模板是一种更高层次的抽象的类定义,用于使用相同代码创建不同类。类模板的定义与函数模板的定义类似,只是把函数模板中的函数定义部分换做类说明,并对类的成员函数进行定义即可。在类说明中可以使用出现在 TYPE_LIST 中的各个类型标识,以及出现在ARG_LIST 中的各变量,例如下述程序定义了一个类模板 。

template <class TYPE, int i>class MyTemplateClass { public: MyTemplateClass( void ) {} ~MyTemplateClass( void ) {} int MemberSet( TYPE a, int b ); private: TYPE Tarray[i]; int arraysize;};

Page 32: 第 12 章 面向对象程序设计的其它技术 12.1 运算符重载 12.2 流类库 12.3 模板 学习目的: ①掌握运算法的重载 ; ②了解流式文件的概念,并能进行

12.3.3 模板应用实例[ 例 12.13] 编写插入排序的函数模板。 #include "iostream.h"#include "string.h"int StringSortCompare(char* s1, char* s2){//字符串数据比较函数 if(*s1<*s2) return -1; else if(*s1==*s2) return 0; else return 1;}template<class TYPE, int (*pCmpFunc)(TYPE, TYPE)>// TYPE 为待排序数据的类型, pCmpFunc 为函数指针变量 , 指向比较函数void StrSort(TYPE Data[], int nStrNum){ for(TYPE *pInsert=Data+1; pInsert<Data+nStrNum; pInsert++) { TYPE tTemp=*pInsert; TYPE *pSorted=pInsert-1; while(pSorted>=Data && pCmpFunc(tTemp, *pSorted)==-1) { *(pSorted+1)=*pSorted; pSorted--; } *(pSorted+1)=tTemp; }}

Page 33: 第 12 章 面向对象程序设计的其它技术 12.1 运算符重载 12.2 流类库 12.3 模板 学习目的: ①掌握运算法的重载 ; ②了解流式文件的概念,并能进行

void main()

{

char *pStrings[100], StrTemp[256], **pNewPos=pStrings;

cout<<"Enter character strings!"<<endl;

while(cin>>StrTemp)

{// 输入字符串数组 *pNewPos=new char[strlen(StrTemp)+1];

strcpy(*pNewPos, StrTemp);

pNewPos++;

}

int nCount=pNewPos-pStrings;

StrSort<char*, StringSortCompare>(pStrings, nCount);// 调用排序函数 cout<<endl<<endl<<"The sorted strings are: "<<endl;

for(int i=0; i<nCount; i++)

{// 输出排序后的结果 char* Data=*(pStrings+i);

cout<<Data<<endl;

delete[] Data;

}

}

Page 34: 第 12 章 面向对象程序设计的其它技术 12.1 运算符重载 12.2 流类库 12.3 模板 学习目的: ①掌握运算法的重载 ; ②了解流式文件的概念,并能进行

由于有了排序函数的函数模板,使用这个模板就可以方便地对各种类型的数据进行排序,例如对整型数组排序,见下面程序片段:

int IntSortCompare(int n1, int n2)

{//整型数据比较函数 if(n1<n2)

return -1;

else if(n1==n2)

return 0;

else

return 1;

}

void main()

{

cout<<"Enter integers!"<<endl;

int data[100], i=0;

while(cin>>data[i])

i++;

StrSort<int, IntSortCompare>(data, i);

cout<<endl<<endl<<"The sorted strings are: "<<endl;

for(int j=0; j<i; j++)

cout<<data[j]<<endl;

}

Page 35: 第 12 章 面向对象程序设计的其它技术 12.1 运算符重载 12.2 流类库 12.3 模板 学习目的: ①掌握运算法的重载 ; ②了解流式文件的概念,并能进行

12.3.3 模板应用实例[例 12.14] 以类模板的方式实现列表#include "iostream.h"#include "string.h"enum ERROR_CODE{ overflow, range_error, success// …};const int MAX_LEN_LIST=200;template<class LIST_ITEM_TYPE>class CList{ public: CList(); //创建列表并初始化列表为空 int Size() const; // 返回列表中的表项数目 bool Full() const; // 列表满则返回 true bool Empty() const; // 判断列表是否为空,如空则返回 true void Clear(); //移除所有表项,清空列表

Page 36: 第 12 章 面向对象程序设计的其它技术 12.1 运算符重载 12.2 流类库 12.3 模板 学习目的: ①掌握运算法的重载 ; ②了解流式文件的概念,并能进行

void Traverse(void(*visit)(LIST_ITEM_TYPE&));

//遍历每个表项,对每个表项应用用户定义的操作 *visit

ERROR_CODE Retrieve(int nPos, LIST_ITEM_TYPE &x) const;

// 如果 nPos代表合法项,将之拷贝到 x ,否则报错 ERROR_CODE Replace(int nPos, const LIST_ITEM_TYPE& x);

// 如果 nPos代表合法项,则用 x 取代该项 ERROR_CODE Remove(int nPos, LIST_ITEM_TYPE &x);

// 如果 nPos代表一合法项,则删除它,做必要的表项移动 ERROR_CODE Insert(int nPos, const LIST_ITEM_TYPE &x);

// 如果 nPos 为表项中的某合法位置则插入,做必要的表项移动。否则报错 protected:

int nCount;

LIST_ITEM_TYPE Items[MAX_LEN_LIST];

};

template<class LIST_ITEM_TYPE>

int CList<LIST_ITEM_TYPE>::Size() const

{ return nCout; }

template<class LIST_ITEM_TYPE>

Page 37: 第 12 章 面向对象程序设计的其它技术 12.1 运算符重载 12.2 流类库 12.3 模板 学习目的: ①掌握运算法的重载 ; ②了解流式文件的概念,并能进行

ERROR_CODE CList<LIST_ITEM_TYPE>::Insert(int nPos, const LIST_ITEM_TYPE &x)

{

if(full())

return overflow;

if(nPos<0 || nPos>nCout)

return range_error;

for(int i=nCout-1; i>=nPos; i--)

Items[i+1]=Items[i];

Items[nPos]=x;

nCout++;

return success;

}

Page 38: 第 12 章 面向对象程序设计的其它技术 12.1 运算符重载 12.2 流类库 12.3 模板 学习目的: ①掌握运算法的重载 ; ②了解流式文件的概念,并能进行

template<class LIST_ITEM_TYPE>

CList<LIST_ITEM_TYPE>::CList()

{

nCout=0;

Items=new LIST_ITEM_TYPE[MAX_LEN_LIST];

memset(Items, sizeof(LIST_ITEM_TYPE)*MAX_LEN_LIST, '\0');

}

template<class LIST_ITEM_TYPE>

void CList<LIST_ITEM_TYPE>::Traverse(void(*visit)(LIST_ITEM_TYPE &))

{

for(int i=0; i<nCout; i++)

(*visit)(Items[i]);

}