汤姆斯旺C++编程秘诀可编辑.doc_第1页
汤姆斯旺C++编程秘诀可编辑.doc_第2页
汤姆斯旺C++编程秘诀可编辑.doc_第3页
汤姆斯旺C++编程秘诀可编辑.doc_第4页
汤姆斯旺C++编程秘诀可编辑.doc_第5页
已阅读5页,还剩13页未读 继续免费阅读

下载本文档

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

文档简介

个祥子:class TAnyClass int x; int y;friend ostream & operator (istream &,TAnyClass &) ;friend bofstream & operator (bifstream &,TAnyClass &) ;public:TAnyClass(); x(0);y(0)TAnyClass(int X,int Y);x(X),y(Y);因为运算符函数的参数不同, 所以编译器能依据其使用的场合在函数间作出区分。这个新的输出流运算符的实现如下所示:ostream & operator( ostream &os, TAnyClass &c)os”x=”c.xendl;os”y=”y.x(istream&is, TAnyClass &c)coutc.x;coutc.y;return is;现在你可以为来自cin 的TAnyClass 对象作提示,再将这些对象写到cout, 并用二进制文件流读、写磁盘文件中的对象。所有这些助作都可以用相对简单的流语句来完成。首先定义两个TAnyClass 对象:TAnyClass original, copy;然后, 用下面这些语句给出提示、读入并确认对象original:Cout oribinal; / 提示并读值coutYour values are: n ;cout original; / 显示进入对象的值接下来, 力了用二进制形式把对象写到磁盘,首先要构造一个二进制输 出文件流。我将它称之为bofs:# define FILENAME “TBCLASS. DAT”bofstream bofs(FILENAME) ;if (! bofs)cerr Error: unable to create FILENAME endl;exit ( 1 ) ;bofs original; / 把类对象写到磁盘bofs. Close() ;/ 关闭文件,送数据到磁盟倒数第二条语句把original 对象写到这个文件流,借助于类中的重载 运算符函数将它的数据存忙到磁盘上。最后一条语句关闭文件。这一步仅当你需要访问这个文件的数据而又处于作用域内时才有必要。为重读该对象, 在关闭输出文件之后拘造一个輸入文件流并象下面这祥读一个original 对象的拷贝:bifstream bifs (FILENAME) ;if (! bifs) cerr “Error: unable to open “ FILENAME copy;/ 把类对象从磁盘读到copy中最后一条语句读文件,并通过调用重载类运算符函数将对象的数据从磁盘装入到名字力copy 的TAnyClass 对象中, 用下列语句显示copy 的值:coutCopy of class object :n ;cout copy;6.3.7 在流中搜尋对数据库工作而言, 程序需要反复挑栋文件中的一些特定记录。你可以 调用下面两个重载成员函数之一, 利用輸入文件流达到上述目的,这两个成 员函数都是从istream 类中继承的:istream & seekg(streampos) ;istream &seekg(streamoff , ios : : seek _ dir) 多streampos 和 streamoff 的数据类型典型地定义成与long 值等价 :typedef long streampos;typedef long streamoff ;不过, 另一个编译器有可能用一个不同的数据类型来定义streampos 和 streamoff , 所以不能把long 值直接传递给 seekg() 。Seekg()的第一种重载格式把输入流定位到一个特定的字节, 第二种格式把输入流从三个由ios:seek_dir 定义的位置之一开始秘动一个偏移量(见表 6. 2)。表 6.2ios:seek-dir常量常量值插 述beg0从文件买开始搜寻cur1从文件的当前位置开始搜寻end2从文件尾开始搜索对于ios:beg 是提供正的偏移量。 对于 ios:cur 提供正的偏移量是为了向文件尾作正向搜寻, 否则使用负的偏移量以便反向搜寻到文件实。对于ios:end 总是提供负的偏移量。为了定位輸出流的内部文件指针, 可以用下面两个从ostream 类继承的重载輸出文件流函数:Ostream& seekp(streampos) ;ostream &seekp(streamoff , ios: :seek_dir) ;这里有一些例子说明如何来用这些函数。(我在这里将使用seekg()。同样seekp()函数也对輸出流起作用 )。首先用前一节的TAnyClass 构造几个要存储到一个磁盘文件中的对象:TAnyClass a(0,l), b(2,3),c(4,5),d(6,7),e(8,9);然后打开一个二进制输出流, 并把这些对象写到磁盘: bofstream bofs( ABCDE. DAT ) ;if ( ! bois) cerr Error: unable to create ABCDE. DATn ; exit (1);bofs a b c d aa bb cc dd ee;可是, 有时我们不愿顺序地从文件中读所有数据,而是想随机地搜寻并读取特定的记录,例如你很可能需要在一个存储姓名和地址数据库文件中送样做。而这时文件包含记录和值如图6.4 所示:记录号 对象数据成员 0 1 2 3 4 /X y x y x y x y x y0 1 2 3 4 6 6 7 8 9 ABCDE.DAT 存储在磁盘 文件中的值图 6. 4 TAnyClass 对象的 ABCDE.,DAT 文件为搜寻3号记录, 应象下面这样定义一个streampos 变量并调用输入文件流对象的继承seekg()成员函数:streampos rn = 3; /记录号 bifs.seekg(sizeof (TAnyClass) * rn);这就把内部文件的指针定位到了3号记录的首字节上。因为0号记录是文件的第一条记录,因此下列语句读取并显示第四条记录 :bifs aa;cout bb;cout cc ;cout cc ;不管该文件存有多少记录, 上述语句都会把文件的最后一条记录装载到CC中, 运行这个程序片段将把对象CC 设置成 (8,9) 。最普通的操作是从文件头开始作正向搜寻。因为我们知道该文件有五条记录, 搜寻4号记录就把内部文件指针定位于文件的最后一条记录。下面语句把最后一个记录 (8,9) 装载到dd。rn = 4; / 从文件头开始搜寻的记录数 bifs. seekg(sizeof (TAnyClass) *rn) ios: : beg) ; bifs dd; cout dd ;为了重新把文件置于文件开头, 也许这是要准备按顺序地从第一条记录重读数据, 可以搜寻0号记录 :bifs. seekg(0) ;注: 为了节省篇幅, 我在这些范例中故意省略了错误检查。用文件流 eof () 成员函数可以检测内部文件指针是否定位于文件尾, 从那里读数据将是一个错误。你也可以用第三章 调用流成员函数 中讨论的流状态设置去检测文件流语句的错误。6.4 其它的文件流技术这里有几个其它的文件流技术, 你不会每天去用, 但也许会发现它们也很方便。6.4.1 打开和关闭文件对象在操纵多个文件的繁忙程序中构造文件流对象可能会影响一个程序的性能。在这种情况下, 你可以提前檢造对象, 并在后来用它们去打开和建立文件。为了构造一个输出文件流, 但又不把它和文件联系起来, 可以这祥定义对象:ofstream ofs;ofs 文件流对象没有联接到文件, 因此你不能在一个输出文件流中用这个对象。为了用该对象写或建立一个文件, 要调用它的open()成员函数:ofs. Open( NEWFILE. DAT ) ;if(ofs)exit(l); / 如果发现错误则退出或者, 你能指定常量 (见表 6. 1 )。 例如, 用一个这祥的语句去覆盖和截断一个现存的文件:ofs. Open(NEWFILE. DAT , :out | ios: :trunc) ;然后 , 你能写这个文件并关闭它 :ofs First line endl; ofs second line endl; ofs Last lineendl ;ofs.close ( ) ;可以用类似的方法构造未打开的输入文件流对象。首先定义一个if. stream 类对象,不要为构造函数指定参数:ifstream ifs;在后来的程序中, 可以调用该对象的open() 成员函数去打开一个现存文件并读出它的内容 :Open( NEWFILE. DAT ) ;if( ! ifs) exit(l) ; while ( ! if s. eof ( ) ) ifs. getline (buffer , BUFLEN , n ) ; cout buffer endl;ifs, close ( ) ;在调用close() 成员函数之后, 你能重新使用文件流对象ofs和ifs去读、写其它文件。6.4.2 获取文件播述符我提及这个问题可能是自找麻烦, 但获取一个文件流对象的内部文件描述符 (有时称之为一个文件句柯 ) 是能够做到的。得到该值后, 你能通过调用操作系统子程序用它去读写文件。如果你决定要用这个技术,你多半要放弃各种把你的程序移植到其它系统的想法。不过这个方法在某些非平凡情形下可能有用。一个 filebuf 类的对象可以向ifstream 和ofstream 提供低层的服务。if- stream 和 ofstream 这两个文件流类声明了一个返回指向其 filebuf 对象的指针的成员函数:filebuf * rdbif ( ) ;可以用结果指针去调用低层的filebuf成员函数。例如, 在我的系统上, 成员函数 fd() 返回一个用整数表示的文件句柄。力了使用这个函数, 并保持代码简单, 要包括 fstream.h 头文件并定义一个调用 fd() 的宏:#include #define FD(s) (s). rdbuf()fd()然后你能构造一个文件流对象并用这个宏指令去获取文件的句柄 : ifstream ifs( ANYFILE. TXT ) ;Cout descriptor- FD(ifs) 和一个operator(bfstream signed char &);/ 其它重载 operator函数friend bf stream &operator (bfstream signed char ) ;/. . . 其它重载 operator函数 public :bfstream (const char * fn): fstream(fn, ios:in | ios:out | ios: : binary) void writeBytes(const void * , int); void readBytes(void* , int) ;BFSTEST. CPP 程序说明了如何使用bfstrean 类。程序包括bfstream. h头并声明一个TAnyClass 类, 它与你在本章其它地方看到的TAnyClass 类相似 :#include bfstream. h class TAnyClass int x; int y;friend ostream&operator (ostream&, TAnyClass &) ;friend istream &operator (bfstfeam &,TAnyClass&); friend bfstream &operator(bfstream&, TAnyClass&); public .TAnyClass(): x(0), y(0) TAnyClass(int X, int Y) : x(X), y(Y)这个类与早期版本之间的唯一区别是重载运算符函数中的bfstream 引用。 用下列语句构造几个读类的对象, 并把它们写到名为ABCDE.DAT 的文件中:#define FILENAME ABCDE. DATTAnyClass a(0,l) , b(2,3), c(4,5) d(6,7) e(8,9); bfstream bfs (FILENAME) ; if (! bfs) cerr Error: unable to create FILENAME endl;exit(1)bfs a b c d aa bb cc dd ee; coutaabbccddee;为了在文件中随机地重写数据, 可遗过调用流的seekp() 成员函数搜寻一个指定的记录。 例如, 下面这四行把 3 号记录 (文件中的第四条记录) 更改为一个新的 TAnyClass 对象:TAnyClass x(123,456); / 构造新的对象long rn = 3; / 定义记录号bfs. seekp(sizeof (TAnyClass)* rn) ;/ 搜寻 3 号记录bfs X; / 把对象写到文件为了用编号读一个指定的记录,可调用流的 seekgO 成员函数, 然后用一个输入流语句从磁盘上把数据装载到一个适当类型的对象 :TAnyClass y; / 构造空的对象bfs. seekg(sizeof (TAnyClass)* rn); / 搜寻3 号记录bfs y;/ 从文件中读取对象用通常的调用 closeO 的方法关闭 bfstream 对象:bfs.close () ;6.5 结束语文件流是在C+ 指导教程和参考手册中最易于恕略的主题之一。使用文件流将为你的文件处理例行工作带来 的各种好处。不过应谈意识到, 在各种 C+ 编译器中以及在正在编写的ANSI C+标淮草案中, 文件流库之间是有一定区别的。就此结束第一部份对C+流的深入探讨。在第二部份 C+ 数据结构的奧秘 中, 你将研究 ANSI C+ 标准草案中接纳的最新的成员之一 模板。第二部份的各章介绍模板并说明如何用它们去建立各种各样 的类数据结拘。一旦你开始在你的代码中使用模板, 你多半会感到奇怪: 为什么所有面向对象的语言都没有包含类似的工具?第二篇 C+ 数据结构的奧秘第七章 精制函数模板使用面向对象技术来编制软件的程序员会很快发现:面向对象的程序设计远谈不上完美。现在让我们来考虑容器类。以设计容器类来保存多种类型的对象对于解化大多数数据存储问题来说似乎是一种理想的办法。然而, 在典型的容器类库中, 存储在容器中的对象都必须是从同一个基类中派生而来。这种情形就如同马路上发生碰撞一祥, 它意味着: 仅仅为了把一系列简单的整数存到一个容器, 而你就必须把这些值封装到从另一个指定基类派生而来的类的对象中。这是个多么可怕的负担! 显總, 暮器只有能存储任意类型的数据、而不是仅仅能存储来 自预定系列的类的对象时才能更有实用价值。解决这个十分别扭的难题的秘決是使用一个称作模板的编程工具。 利用类模板, 你就可以以蓝圓的形式来设计困数和类这獎似于一本书的提纲或概要。编译器使用模板来拘造程序所需的实际的函数和类就像作家把书的提纲扩展充实到各完成的章节一样。容器一旦被设计为模板, 它就变成了适应性非常强的工具。编译器能够重建类属模板来保存你所需要存储的任何类型的数据。本章, 即第二篇的开头部分,先介绍函数模板。第八章将涉及类模板。在阅读本篇大部分其它章节之前, 你需要了解本篇开头两章的一些背景信息。注: 尽管 ANSI C+ 标准草案已经采纳了模板, 但目前并非所有的C+ + 编译器都提供模板。 總而不管怎么祥, 绝大多数C+编译器将广泛地迅速提供摸板, 这一点是毫无疑间的。7.1什么是挺板?模板是一种描述函数或类的特性的蓝图。模板对于创建类属函数来说尤其有用例如, 可重新排列不同类型对象的排序函数。你也可以使用模板来创建可以与许多类型的对象一起工作的类属类。无论设计为函数或类, 所有模板都以template 关键字和一个形参表开头。 例如: 带一个参数的模板可以下述形式开头:template跟在 template 关键字后面的角括号 () 是必需的, 括号中的单词class 也是必须的。从字面上的意思来说, 在此上下文class 意为 一个用户定义的或固有的类型。字母T 标识这个类型。注意单词class与C+类没有任何关系。你也可以使用其它的标识符来标识类型参数, 下面模板的参数名为Avocadotemplateclass Avwado当然, 一些严肃的程序员是不会把他们的标识符命名为Avwado 的, 我只不过想使读者知道这一点:即参数名是可以任意挑选的。T是一个比较好的选择,因为它使人联想到单词Type 。一个模板声明可以列出多个参数。下面这个模板有两个参数,T1 和T2:template多个参数之间须用逗号隔开, 每个参数都必顿重复使用单词class,对所能声明的参数数目没有限制。下面是有三个参数的模板: template象T1 ,T2和T3这样的参数可在其它的模板声明中再次使用这些参数名的作用域仅在谈模板范围内。只要有可能, 最好是使用描述性的参数名。我喜欢把我的标识符以T 开头, 提醒我注意它们的模板状态:template这一行是一个容器类模板的开实, 族容器类可以存储以另一个类型TIndex 的值来索引的类型 TData 的对象。 类似TData 和TIndex 这样的参数是随后将指定的实际数据类型的位置标志符。TData 或TIndex 或许由int 、double、char 或其它数据类型来代替, 它们也许还是指計或者说它 们可以用来引用类或结构。在创建模板的同时, 你也说明了你打算使用的未指定类型的对象。模板将使下面这个动作成为可能, 即以一种完全通用的方法来设计函数或类而不必预先说明将被使用的每个对象的类型。7.2函数模板有两种奥型的模板 函数模板和类模板。本节将介绍函数模板并列出一些范例程序。在第八章, 我将解释如何创建和使用类模板。7. 2.1 函数模板语法不管它们的性质如何, 所有的函数模板都具有同祥的基本格式。图7.1 说明了单参数的函数模板。角括号括起参数表模板关键字参数T作为提供的类型的位置标志符主数声明template void f(T param) /函数体 该函数使用T来定义名为param的变元类型如图 7. 1 所示,template 关键字和角括号中的classT一起开始了模板的构造, 它把T指明为使用模板时将被指定的类型的位置标志符。紧接着的是函数声明,它利用T 把parm 定义为将传递到函数的变元。模板中的每一个参数在函数参数表中必须至少使用一次。下面所示的声明是不允许的。template void f(Tl paraml) / ?/ 函数体这个函数模板声明了两个参数T1 和T2, 但是函数本身却只使用了T1 来定义 parml 。正确的声明应谈是在函数参数表中至少使用T1和T2 各一次:templateclass T1, class void f(Tl paraml, T2 param2)/-. 函数体你可以在能正常使用内部类型的任何地方使用模板的位置标志符来定义变量、指针、换型及其它对象。 然而, 参数表是不能为空的, 根据它们的性质, 函数模板可产生多重函数以对多种对象类型进行操作。假如模板没有参数, 那么只能产生一个函数, 这也就没有什么理由要把函数写为模板了。7.2. 2 min 和 max 函数模板通过考察几个例子, 我们就可以很容易地推测出函数模板的价值。程序清单7.1 声明了三个典型的函数模板 min() , max() 和 inrange() 。# define MINMAX_H # define MINMAX_H / 避免多个# includestemplateT max(T a, T b)return (a b) ? a,btemplate T min (Ta ,Tb)return(ab)?a;btemplateint inrange (T a, T b, T c)return (b = a)& (a = c)#endif /MINMAX_H该头文件声明并实现了函数模板。每个模板以下列所要求的前导开头: template在这个声明中,T 是对象的类型, 谈对象由函数对其进行操作。 在这一行下的函数标题声明了名为 max() 的函数模板:templateT max(Ta,T b)上面第二行把max() 声明为返回类型为T的值的函数。读函数要求有两个变元 a 和b, 旦都属类型T。请记住,T 代表任意类型它的精确性质在这个阶段是不重要的。 函数 min() 也以类似的方式定义。函数体完成模板的动作:return(ab)? a : b;如果a 的值大于b, 那么谈语句返回a, 否则返回b。变量 a 和b 的类型都是T(无论T 是什么类型) , 该函数返回类型为T 的值, 因而显想能返回a 或b。函数模板 min() 可以以英似的方法编写,但返回的值有所不同,若a 小于b 则返回a 否则返回 b。注 : 在上一个例子中, 编语器假定a 和b 是可以比较的, 因此最终结果总是为真,否则编谦器将不能从模板中产生函数。例如,如果 a 和 b 是类对象, 那么在编译器能够编译之前诛就必须提供重载operator 和operator 函数。如果你在掌握函数一模板语法时仍还有麻烦, 那么你可以把max() , min() 和 inrange()中的T用int 来替换,并想略模板前导。max() 函数中的T 被int (用黑体字表示) 替换后成为下述形式 :int max(int a,int b)return (a b) ? a : b;用 int 来代替T恰恰就是编译器从模板产生实际函数时所做的工作,当然只有在程序使用模板的情况下才会发生这个动作。模板的使用決定了替換模板所声明的参数的数据类型。程序清单7. 2 MINMAX.CPP 针对min() , max() 和 inrange ()说明了这个概念的几种情况。程序清单 7. 2 MINMAX. CPPinclude iostream. hinclude minmax. hint main()int a=123 b= 456;cout “min(123,456) = min(a, b) endl;cout max(123, 456) =” max (a, b) endldouble c = 3. 14159, d = 9.87654; cout min(3. 14159, 9. 87654) =”min(e,d)endl;cout min(3. 14159, 9. 87654) =”max(e,d)endl;char e = q , f = a ;cout min( q , a ) = min(e, f) endl; cout maxC q , a ) = max(e f) endl;short sl 10, low = 5, high 15; cout s1 = s1 endl; if (inrange(sl, low, high)cout low = sl high endl;else

温馨提示

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

评论

0/150

提交评论