面向对象程序设计c++_第1页
面向对象程序设计c++_第2页
面向对象程序设计c++_第3页
面向对象程序设计c++_第4页
面向对象程序设计c++_第5页
已阅读5页,还剩162页未读, 继续免费阅读

下载本文档

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

文档简介

面向对象开发韩宏3模块化3.1模块化的概念(Modularity)

我们在软件设计中最求什么?

更快

更小的空间

易用性

健壮性易于维护易于扩展1)客户常常要求增加新功能

我们需要修改代码2)新的和好的算法需要替代老的算法我们需要修改代码3)数据存储格式的变化我们需要修改代码4)修改我们的错误我们需要修改代码……

在软件的世界里,变化是永恒不变的主题!!!!

为了拥抱变化,我们希望用什么支持?模块化:它意味着软件可以被方便的演化,通过象摆积木一样简单的替换其某个部分。.SystemASystemBSystemC

结构化编程的模块化好吗?它比过去的语言有进步,但还不够.

CaseStudymodularityinprocedurallanguage

SimpleExampleHelloworldHellowordinClanguage#include<stdio.h>voidmain(){char*p=“helloworld”;

printf(“%s\n”,p);

getchar();}HelloworldinPascallanguageuses

SysUtils;varS:String;beginS:='helloworld';

WriteLn(S);Sleep(4000);end.我们从这个例子中学到了什么?

1)变量

2)函数/方法3)函数的集合以及它们相互联系形成了一个程序

在helloworld的例子中,main()is程序的入口点,它重用了printf

和getchar

两个函数.gecharprintfmainprogram1program2program3program4Libfun1,fun2,fun3….#include<stdio.h>intfoo1(char*filename){

printf(“%s\n”,filename);//reusethefunctionprintf.

CreateFile(filename,…)…}

我了使程序模块化并易于重用,程序被分成许多小块,就像积木一样.每个原子的块就是函数和/方法,通过调用函数我们重用它.

通过将程序分块,我们能够只改变某个块的实现而不改变整个程序

(并不完全这样).改变“积木块”的实现而不改变整个程序procedureinstall(DestineDir:String);Var

FileList:TStringList;I:Integer;Begin

FileList:=GetFilesToCopy;tryforI:=0toFileList.CountdoifnotCopyFileToDestineDir(FileList.Items[I],DestineDir)thenraiseException.Create(‘intallfailed:copyfile’+FileList.Items[I]);except

DeleteFilesCopied;raise;end;End;实现1functionCopyFileToDestineDir(SourceFile:String;DestineDir:String):BooleanVar

FromF,ToF:FileofByte;

NumRead,NumWritten:Integer;

Buf:array[1..2048]ofChar;Begin

AssignFile(FromF,SourceFile);

AssignFile(ToF,DestineDir+‘\’+ExtractFileName(SourceFile));

Reset(FromF);

Rewrite(ToF);tryrepeat

BlockRead(FromF,Buf,SizeOf(Buf),NumRead);

BlockWrite(ToF,Buf,NumRead,NumWritten);until(NumRead=0)or(NumWritten<>NumRead);//readwhole//ornotspacetowritefinally

CloseFile(DesF);

CloseFile(SrcF);ifNumread=0thenResult:=TrueelseResult:=False;end;End;实现2functionCopyFileToDestineDir(SourceFile:String;DestineDir:String):BooleanBegin{TheCopyFileisAPIofWin32}Result:=CopyFile(SourceFile,DestineDir+‘\’+ExtractFileName(SourceFile));End;

如果我们有CopyFileToDesitineDir的好实现,我们仅仅修改CopyFileToDestineDir

而不改动函数Install.快速回顾

Now,Wehavetwobenefits:现在我们有了两大好处

1)通过调用函数重用代码

2)只改变需要变动的代码,不修改其它的代码

现在,这两条让我们象堆积木一样写程序了

(易于替换,易于修改,子函数就像使积木块).但真的是这样吗???3.2模块化1—

走向封装

请想想前面操作文件的例子.

首先,我们申明了一个文件变量.

其次我们用fopen(c)/assignfile(pascal)+reset/append/rewrite打开了一个文件…

使用文件变量读写文件.

最后关闭了文件.

问题:

(1)我们一遍一鞭做了相似的工作.有什么办法简化它吗?

(2)如果我们不打开文件而直接用Read/Write函数,这就是错误的。如果我们用另一个文件变量去read/write这也是错误的.错误(1)

varF,F2:TextFile;

Ln:String;begin

AssignFile(F,FileName);

ReadLn(F,Ln);//error,notopenfile.end;Error(2)begin

AssignFile(F,FileName);

Reset(F);

ReadLn(F2,Ln);//erroruseanotherfilevariableEnd;

如果我们不显式地操作文件变量,这些错误就可以避免。如果文件读写相关的函数自动地使用正确的文件变量,我们不仅可以避免错误,还可以解决第一个问题:简化文件操作的过程.

我们想象文件操作本身就是一个实体,它可以完成相关操作。任何相关的部分,比如文件变量是它内部的一个组分,而不合外部其它的什么相关联。而读写操作自动和内部的组分:文件变量关联:Var

Ln:String;Begin

FileOperation.Open(FileName);

FileOperation.ReadLn(Ln);….

FileOperation.GetFileName;

FileOperation.CountLine;

FileOperation.Close;End;

这种形式,文件变量对程序员是不可见的.因此,我们不会在读写操作中用错文件变量。

另外,我们简化了操作流程,因为我们不需要一遍又一遍地使用文件变量

(在所有的调用中比如

open,closefile,read/write).

更有趣的是,我们将一个文件的信息收集到一个实体中.比如,FileOperation.GetFileName

可以获取文件名,FileOperation.CountLine

可得到文本文件中的一行.

为了达到这个目标,我们向模块化迈出第一步:

封装.

Inthisway,dataandoperationisasingleentity,likechip,inwhichstorageandlogiccircuitmakeupacomponent在这种方式下,数据和操作数据的函数是一个整体,就像是集成芯片,在当中其存储部分和逻辑电路形成了一个组件.

如何达到这个目标??

首先我们尝试用已知的方式来做:Var

FileName,Ln:String;F:TextFile;

CountLine:Integer;Begin

AssignFile(F,FileName);

Reset(F);

CountLine:=0;whilenotEof(F)dobegin

ReadLn(F,Ln);

CountLine:=CountLine;end;

CloseFile(F);End;

Ifwewanttomakethemworkasawhole,thesuitablecandidateisstruct/record如果我们希望它们能作为一个整体(象芯片一样)工作,适合的选择是Struct/record.

TTextFileInfo=recordF:TextFile;

FileName:String;

CurrentLine:Integer;

TotalLine:Integer;Mode:Integer;//theopenmodeofthefileend;

PTextFileInfo=^TextFileInfo;

我们用TTextFileInfo

重构以前的代码Var

Ln:String;Info:TTextFileInfo;Begin

AssignFile(Info.F,Info.FileName);

Reset(Info.F);

Info.CountLine:=0;whilenotEof(Info.F)dobegin

ReadLn(Info.F,Ln);

Info.CurrentLine:=Info.CurrentLine;end;

CloseFile(Info.F);End;

这个代码和第一个版本的代码非常相似,但我们将所有相关信息放在一个整体中

(在Info中).

这样的代码可以重用,因为它作为一个整体在工作.现在我们尝试解耦这块代码变成几块代码以便于重用.(1)打开文件

functionOpenTextFile(Info:PTextFileInfo):TTextFileInfo;(2)读取一行

functionReadLine(FileInfo:

PTextFileInfo;var

Ln:String):Boolean;Thereturnvaluerepresentsifreadsuccssfully.(3)写一行

procedureWriteLine(FileInfo:PTextFileInfo;Ln:String);(5)关闭文件

procedureCloseTextFile(FileInfo:PTextFileInfo);Var

Ln:String;Info:TTextFileInfo;Begin

Info.Name:=FileName;

Info.Mode:=_ModeRead;//thisisaconstant

OpenTextFile(@Info);whileReadLine(@Info,Ln)do;//donothinginfielddo

CloseFile(@Info);End;

现在的代码比上一个版本更加简化:reset和Eof

函数不存在.我们不用显式计算当前行.

文件变量不存在,在每一个文件相关操作中,我们根本没用到它.

代码可以重用,如果使用TTextFileInfo.现在我们看看最新版本的代码:const

_ModeRead=0;_ModeWrite=1;_ModeAppend=2;typeTForm1=class(TForm)Button1:TButton;OpenDialog1:TOpenDialog;Memo1:TMemo;procedureButton1Click(Sender:TObject);private{Privatedeclarations}public{Publicdeclarations}end;

TTextFileInfo=recordF:TextFile;

FileName:String;

CurrentLine:Integer;

TotalLine:Integer;Mode:Integer;end;

PTextFileInfo=^TTextFileInfo;

procedureOpenTextFile(Info:PTextFileInfo);functionReadLine(Info:PTextFileInfo;var

Ln:String):Boolean;procedureWriteLine(Info:PTextFileInfo;Ln:String);procedureCloseTextFile(Info:PTextFileInfo);varForm1:TForm1;implementation{$R*.dfm}functionGetLineCount(FileName:String):Integer;varF:TextFile;

Ln:String;begin

AssignFile(F,FileName);

Reset(F);Result:=0;whilenotEof(F)dobegin

ReadLn(F,Ln);Result:=Result+1;end;

CloseFile(F);end;procedureOpenTextFile(Info:PTextFileInfo);begin

AssignFile(Info^.F,Info^.FileName);caseInfo^.Modeof_ModeRead:begin

Info^.TotalLine:=GetLineCount(Info^.FileName);

Reset(Info^.F);

Info^.CurrentLine:=0;end;_ModeWrite:begin

Info^.TotalLine:=0;

Rewrite(Info^.F);

Info^.CurrentLine:=0;end;_ModeAppend:begin

Info^.CurrentLine:=GetLineCount(Info^.FileName);

Info^.TotalLine:=Info^.CurrentLine;

Append(Info^.F);end;elseraiseException.Create('Notsupportthemode');end;end;functionReadLine(Info:PTextFileInfo;var

Ln:String):Boolean;beginResult:=False;ifEof(Info^.F)thenExit;

ReadLn(Info^.F,Ln);Result:=True;

Info^.CurrentLine:=Info^.CurrentLine+1;end;procedureWriteLine(Info:PTextFileInfo;Ln:String);begin

Write(Info^.F,Ln);

Info^.CurrentLine:=Info^.CurrentLine+1;

Info^.TotalLine:=Info^.CurrentLine;end;procedureCloseTextFile(Info:PTextFileInfo);begin

CloseFile(Info^.F);end;{Herewereadlineintoamemo,infirstline,weshowthetotallineofthefileusingthefieldTotalLineofTTextFileInfo.Intheendofeveryline,weappendthelinenumberusingthefieldCurrentLineofTTextFileInfo}procedureTForm1.Button1Click(Sender:TObject);var

FileOper:TTextFileInfo;

Ln:String;beginifnotOpenDialog1.ExecutethenExit;Memo1.Lines.Clear;

FileOper.FileName:=OpenDialog1.FileName;

FileOper.Mode:=_ModeRead;

OpenTextFile(@FileOper);Memo1.Lines.Add('TotalLine:'+IntToStr(FileOper.TotalLine));whileReadLine(@FileOper,Ln)doMemo1.Lines.Add(Ln+'currentLine:'+IntToStr(FileOper.CurrentLine));Memo1.Lines.Add('TotalLine:'+IntToStr(FileOper.TotalLine));

CloseTextFile(@FileOper);end;继续进化

现在的版本比较好,但并没有从根本上满足我们.因为函数是函数(opentextfile,readline.etc.).数据是数据

(TTextFileInfo).函数和数据是分离的,即使数据本身是集中和封装的(集中封装在结构TTextFileInfo中)我们的目标是:数据和函数的集中与封装.请回顾一下我们的目标如下代码所示:

Var

Ln:String;Begin

FileOperation.Open(FileName);

FileOperation.ReadLn(Ln);….

FileOperation.GetFileName;

FileOperation.CountLine;

FileOperation.Close;End;

在上面的代码中,我们不需要传递TTextFileInfo到函数中.FileOperation

代表了数据和函数的集合体.

为了达成这种形式的代码,我们需要继续进化代码.

注意‘Fileoper.Close()’,那个小点意味FileOper是

record/structure.整个表达式说明它是个函数。这意味着结构的一个成员变量是函数。

如何将一个函数赋值给一个结构体的成员变量?那个成员变量必须是函数指针.

好,我们需要变换结构体TTextFileInfo的申明.

TCloseFile=procedure(Self:PTextFileInfo);

TOpenTextFile=procedure(Self:PTextFileInfo);

TReadLine=function(Self:PTextFileInfo;var

Ln:String):Boolean;

TWriteLine=procedure(Self:PTextFileInfo;Ln:String);

TTextFileInfo=recordF:TextFile;

FileName:String;

CurrentLine:Integer;

TotalLine:Integer;Mode:Integer;//theopenmodeofthefileOpen:TOpenTextFile;Close:TCloseTextFile;

ReadLn:TReadLine;

WriteLn:TWriteLine;end;

在使用TTextFileInfo的变量之前,我们需要把相关的函数指针赋值给相关字段.为了简化操作,和避免出错我们申明了一个新的函数InitTextFileInfo.

procedureInitTextFileInfo(Info:PTextFileInfo);overload;procedureInitTextFileInfo(Info:PTextFileInfo;FileName:String;Mode:Integer);overload;

变换后的代码如下:

Var

Ln:String;Info:TTextFileInfo;Begin

InitTextFileInfo(@Info,FileName,_ModeRead);

Info.Open(@Info);whileInfo.ReadLn(@Info,Ln)do;//donothinginfielddo

Info.Close(@Info);End;

全部代码如后:

PTextFileInfo=^TTextFileInfo;

TCloseFile=procedure(Self:PTextFileInfo);

TOpenTextFile=procedure(Self:PTextFileInfo);

TReadLine=function(Self:PTextFileInfo;var

Ln:String):Boolean;

TWriteLine=procedure(Self:PTextFileInfo;Ln:String);

TTextFileInfo=recordF:TextFile;

FileName:String;

CurrentLine:Integer;

TotalLine:Integer;Mode:Integer;Open:TOpenTextFile;Close:TCloseFile;

ReadLn:TReadLine;

WriteLn:TWriteLine;end;

procedureInitTextFileInfo(Info:PTextFileInfo);overload;procedureInitTextFileInfo(Info:PTextFileInfo;FileName:String;Mode:Integer);overload;….procedureInitTextFileInfo(Info:PTextFileInfo);begin

Info^.Open:=OpenTextFile;

Info^.Close:=CloseTextFile;

Info^.ReadLn:=ReadLine;

Info^.WriteLn:=WriteLine;end;procedureInitTextFileInfo(Info:PTextFileInfo;FileName:String;Mode:Integer);begin

InitTextFileInfo(Info);

Info^.FileName:=FileName;

Info^.Mode:=Mode;end;procedureTForm1.Button1Click(Sender:TObject);var

FileOper:TTextFileInfo;

Ln:String;beginifnotOpenDialog1.ExecutethenExit;Memo1.Lines.Clear;

InitTextFileInfo(@FileOper,OpenDialog1.FileName,_ModeRead);

FileOper.Open(@FileOper);

Memo1.Lines.Add('TotalLine:'+IntToStr(FileOper.TotalLine));whileFileOper.ReadLn(@FileOper,Ln)doMemo1.Lines.Add(Ln+'currentLine:'+IntToStr(FileOper.CurrentLine));Memo1.Lines.Add('TotalLine:'+IntToStr(FileOper.TotalLine));

FileOper.Close(@FileOper);end;继续挺进,我们将创造对象!!!

现在的版本比较令人满意.然而,TTextFileInfo的每一个函数都要求传入一个参数:@Info,这意味着TTextFileInfo

的成员函数必须传递指向Info自己的指针

(现在我们称其为self指针),形式如下:

FileOper.Open(@FileOper);

这种形式不简洁,我们依然可能传错参数,比如,把另外一个FileO的指针传递进去:FileOper.Open(@FileO);这样传递错误,在编译期不会报错,但实际逻辑是错的,在运行期就会出错。。我们的目标是:

FileOper.Open;这种方式的关键和本质优越性在于,他保证了Open只会操作自己的数据,而不可能操作到别人的数据(比如:FileOper.Open(@FileO))。

棘手的问题:但如果不传递Self指针,成员函数是不能工作的!!!

如果我们通过其它方式传递Self指针,就可以不在成员函数中显式传递Self指针了。

函数从哪里获取这个Self指针呢?

Registerorstack.pascal

和c语言是通过Stack传递函数的参数:调用用者将参数值压栈,被调用的函数从栈上获取参数值.

参数也可以从寄存器传递,delphi缺省使用了Register和Pascal方式混用的方法.

现在,我们可以使用寄存器传递Self指针.ECXregister大小式4bytes,它和指针的大小(32位机)一样.

基本想法如下:

我们有一个TTextFileInfo类型的变量叫FunctionEntity.对于调用者代码如下:…(1)把FunctionEntity的指针@FunctionEntity

赋值给ECX(2)调用FunctionEntity.function1…对于function1代码如下:

var

SelfPointer:PTextFileInfo;begin(1)将ECX的值赋值给变量

SelfPointer

(2)和FunctionEntity的成员相关的操作都用SelfPointer达成.end;

这样,我们从成员函数的参数中移走了Self指针,但实际通过ECX传递给了它.OpenTextFile函数修改如下:procedureOpenTextFile;varInfo:PTextFileInfo;begin

asm

movInfo,ECX;//inoldcodeinfoisargumentoffunctionend;…

AssignFile(Info^.F,Info^.File);…End;Mov是汇编语言的命令,其意思是将ECX的值赋值给变量Info

调用方的代码修改如下:

asm

movECX,P;//passselfpointertoecxend;FileOper.Open;//theargumentofselfpointerdoesn’texist

完整代码如下:

PTextFileInfo=^TTextFileInfo;

TCloseFile=procedure;pascal;//usepascalenforcecompilerusepurepascalway//becausewedon’twanttocompilerusethemif//possible(ifuse@varinafunctioncalling,eaxcould//beused,becauseofleaeax,[ebp+…].)

TOpenTextFile=procedure;pascal;

TReadLine=function(var

Ln:String):Boolean;pascal;

TWriteLine=procedure(Ln:String);pascal;

TTextFileInfo=recordF:TextFile;

FileName:String;

CurrentLine:Integer;

TotalLine:Integer;Mode:Integer;Open:TOpenTextFile;Close:TCloseFile;

ReadLn:TReadLine;

WriteLn:TWriteLine;end;

procedureOpenTextFile;pascal;functionReadLine(var

Ln:String):Boolean;pascal;procedureWriteLine(Ln:String);pascal;procedureCloseTextFile;pascal;procedureInitTextFileInfo(Info:PTextFileInfo);overload;procedureInitTextFileInfo(Info:PTextFileInfo;FileName:String;Mode:Integer);overload;procedureOpenTextFile;varInfo:PTextFileInfo;begin

asm

movInfo,ECX;end;

AssignFile(Info^.F,Info^.FileName);caseInfo^.Modeof_ModeRead:begin

Info^.TotalLine:=GetLineCount(Info^.FileName);

Reset(Info^.F);

Info^.CurrentLine:=0;end;_ModeWrite:begin

Info^.TotalLine:=0;

Rewrite(Info^.F);

Info^.CurrentLine:=0;end;_ModeAppend:begin

Info^.CurrentLine:=GetLineCount(Info^.FileName);

Info^.TotalLine:=Info^.CurrentLine;

Append(Info^.F);end;elseraiseException.Create('Notsupportthemode');end;end;functionReadLine(var

Ln:String):Boolean;varInfo:PTextFileInfo;begin{Forcallerofthisfunction,becausereadlnusevariableparameter,soitneedgetaddressofLn,theassmblelanguageisleaEax,[ebp-$4],whichmeansthateaxisoverridewhencallthisfunction.HencewehavetouseECXnotEAXtopassselfpointer.usestackisanotherway.}

asm

movInfo,ECX;end;

Result:=False;ifEof(Info^.F)thenExit;

ReadLn(Info^.F,Ln);Result:=True;

Info^.CurrentLine:=Info^.CurrentLine+1;end;procedureWriteLine(Ln:String);varInfo:PTextFileInfo;begin

asm

movInfo,ECX;end;

Write(Info^.F,Ln);

Info^.CurrentLine:=Info^.CurrentLine+1;

Info^.TotalLine:=Info^.CurrentLine;end;procedureCloseTextFile;varInfo:PTextFileInfo;begin

asm

movInfo,ECX;end;

CloseFile(Info^.F);end;procedureTForm1.Button1Click(Sender:TObject);var

FileOper:TTextFileInfo;P:PTextFileInfo;

Ln:String;

ReadResult:Boolean;beginifnotOpenDialog1.ExecutethenExit;Memo1.Lines.Clear;

InitTextFileInfo(@FileOper,OpenDialog1.FileName,_ModeRead);P:=@FileOper;//********thecodecouldaddedbycompiler*******

asm

movECX,P;end;//********thecodecouldaddedbycompiler*******

FileOper.Open;Memo1.Lines.Add('TotalLine:'+IntToStr(FileOper.TotalLine));

whileTruedobegin

//********thecodecouldaddedbycompiler*******

asm

movECX,P;end;//********thecodecouldaddedbycompiler*******ifnotFileOper.ReadLn(Ln)thenBreak;Memo1.Lines.Add(Ln+'currentLine:'+IntToStr(FileOper.CurrentLine));Memo1.Lines.Add('TotalLine:'+IntToStr(FileOper.TotalLine));end;

//********thecodecouldaddedbycompiler*******

asm

movECX,P;end;//********thecodecouldaddedbycompiler*******

FileOper.Close;end;

现在成员函数比如

opentextfile

是较好的,因为Self指针不存在了。但对于调用方,我们加入了许多向下面一样的汇编代码:

asm

mov

ecx,…..end

对于调用方,和以前的代码相比,新版本似乎更加的丑陋,不方便.如果仅仅如此,我情愿象以前一样每次传递Self指针.

难道我们的努力是无效的????

呼唤新的编译器:

现在我们只有求助于编译器了。

如果我们能够造一个新的编译器,它能自动加入那些汇编代码

(asm…end包起来的那些代码),那我们的目标就达到了.如果这样,不论是调用方还是被调用的函数都不需要显式地传递Self指针

(虽然必须传递它,但相关代码被编译器自动加入了).多么幸福的事啊!!

这个伟大的工作可以被编译器完成吗?让我们分析代码.

如果我们定义一种新的类型叫class,并且把TTextFileInfo

申明称位Class而不是record

TTextFileInfo=class//oldversionisrecord.….end;

编译器当发现调用Class的成员函数就可以加入相关汇编代码,否则编译器怎么知道哪里应该加入,比如事Record类型,它自然不敢加入。

现在,我们要问,什么是一个Class:Class是一个实体,它存在一组函数可以操作属于自己的数据。这些自己的数据是通过Self指针来访问的。

Class类型的变量其实是一个结构.Class是一个标记它能辅助编译器自动加入传递Self指针的汇编代码。

仅仅有这个新的类型Class还不足以使编译器自动加入汇编代码.

对于调用方,这可以很好地做到。只要它发现调用Class类型变量的成员函数就可以加入汇编代码。

但对于成员函数呢?下面使老的申明方式:

procedurefunctionName(…..);

这种申明方式无法表明这个函数是否是Class的成员函数。因此编译器不敢在该函数的开始部分自动加入汇编代码。只有Class的成员函数才应该加。

如果我们在函数的申明中加入一些信息使函数和Class联系起来,那么编译器就敢在这些函数中加入汇编代码了。

Indeclaration:

className=class…

functionfunctionName(…)end;Inimplementation:functionClassName.functionName(…)在我们的例子中:

TTextFileInfo=class…procedureOpen;end;

TTextFileInfo.Open;begin…End;

这种方式下,,open函数属于TTextFileInfo.而TTextFileInfo又是Class类型,于是我们将Open函数和一个Class联系起来了。

但编译器发现open函数属于classTTextFileInfo,它就可以安全地加入汇编代码在Open函数的开始。并且可以安全地在调用这些函数之前加入传递Self指针的汇编代码,因为

FileOper

是classTTextFileInfo,并且FileOper.open

是该class的成员函数,成员函数的调用需要传递self指针.

请想象一下,如果我们象上面一样实现了一个编译器,一些有趣的事情发生了.

TTextFileInfo的结构将变化.

它的哪个部分将变化?

提示:如何调用成员函数?FileOper.Open.Open是TTextFileInfo的一个成员变量,它存放了函数指针.

对于structure/record,我们必须这样做.

在新的编译器中,对于类型位Class的Variable,可以将variable.function的调用形式直接解释成对某个成员函数的调用。这意味着我们不需要申明那些保存函数指针的成员变量.

这很好,节约了空间.老的方式用这些成员变量保存相关函数指针。TTextFileInfo=record

F:TextFile;

FileName:String;

CurrentLine:Integer;

TotalLine:Integer;Mode:Integer;

Open:TOpenTextFile;Close:TCloseFile;

ReadLn:TReadLine;

WriteLn:TWriteLine;end;TTextFileInfo=class//

F:TextFile;

FileName:String;

CurrentLine:Integer;

TotalLine:Integer;Mode:Integer;

//functionpart,notallocatememory…end;

对

TTextFileInfo,有四个成员变量保存函数指针。每个指针需要4Bytes.我们保留了4x4=16bytes.这意味着如果有N个成员函数,每个TTextFileInfo类型的变量将节省4xNbytes.

如果有M个该类型的变量,将节省4xNxMbytes.

同时我们还省去了初始化这些函数指针成员变量的函数。因为这一切在编译期是确定的。

我们最新的代码将如下工作:

TTextFile=class…//下面的Open申明仅仅是个标记,没有专门分配内存和它关联.procedureOpen;end;

TTextFile.Open;begin

asm//hiddenbycompiler…end;….end;

对于调用方

var

FileOper:TTextFile;begin

asm//被编译器隐藏

…end;{为了调用该函数,仅仅简单地调用了open函数,这个调用的大门代码和简单的函数调用一样}

FileOper.Open;end;

快速回顾:

(1)结构化编程的问题激发我们寻求新的方法封装我们的行为和数据.

(2)首先我们将相关数据集合在一起,定义为单个record/structure.

但函数和数据是分离的,我们需要将Structure的指针传递给函数.

OpenTextFile(@Info);

我们需要如下的封装形式:

Info.Open(@Info);(3)我们尝试在结构中加入新的成员变量,它们存储了函数指针。这样我们将相关函数指针设定到这些成员变量,达成了我们第二步的目标.

但每次调用这种类型的函数均有如下形式:info.open(@Info),我们依然不得不传递structure变量的指针,我们称它为selfpointer.这依然存在问题,因为可能操作到别人的数据,如果传递错误的指针.我们需要这样的形式

Info.Open;(4)Self指针必须被传递,但我们希望有一个隐式传递的方式.

有两种传递参数的方法:registerorstack.我们选择了Register.用ECX寄存器传递self指针.

但调用这种函数时,将Self指针传递给ECX.在被调用的函数中,我们从ECX中获取函数指针.

这样,我们的函数中不用传递Self指针

asm

movECX,…end;

Info.Open;

但我们加入了大量的汇编代码.这比第三步更加丑陋。然而,这是优雅的解决方案的基础.(5)我们希望汇编代码能自动被编译器加入,在源代码被编译时。那么我们只有为这样的结构定义新的类型,比如叫Class,以便给编译器提供足够的信息来完成这项自动添加汇编代码的工作.

最后我们定义了新的类型叫做class并构造了和他相关的语法.

AClassName=classField:Type;….procedurefunction1….;end;AClassName.function1…beginEnd;Forcaller:Var

VarName:AClassName;beginVarName.function1;end;

如此,我们不仅优雅地实现了我们的最终目标,而且还消除了那些存放函数指针的成员变量。

我们得到了如下的形式:

info.open;

因为有新编译器的支持,我们具有了将数据和行为(函数)封装的能力---就是行为(函数)只作用在自己的数据上(因为编译器一定是将变量自己的Self指针传递给自己的函数,而不会象人一样会出错,传成其它变量的指针)。

从语法角度,我们的演化如下:data1,data2,…datanopen(@info)info.open(@info)assemblecode+info.open;info.open;

对于新的编译器,我们希望它提供了两种新的概念:classandvariableofclass.I

Class很象结构体,但有自己的行为(函数)。我们称Class为类。

Variableofclass本质上就是一个结构体类型的变量。但它有自带的行为(函数)作用于其上。我们称它是Object--对象.

我们是不是仅仅改变了语法形式?新的风格也是通过结构化编程支持的,这又是否意味着新的风格比旧的风格层次低?

新风格不仅仅是语法的变化,请记住困扰我们的问题.

在这种风格背后,是一种新的方法学:

它支持数据和行为的封装.

这个封装意味着:

对象只操作自己的数据.

请回忆演进过程中的一种过渡形式:

info.open(@info)

我们称@info为self指针,因为变量Info在调用自己的函数info.open,并将自己的指针传递给了自己的函数(info.open(@info)).

但这种风格:info.open(@info),我们可能将错误的指针传递,但Info.open这种形式却阻止我们犯错.这个机制是语言是编译器保证的只操作自己的数据.

3.3面向对象编程

封装

在前面一节,我们通过摸索得到了一种新的编程风格.我们称新的类型叫class,称variableofclass为object.

实际上,这就是面向对象编程的一部分—封装.

它保证了以下原则:

只操纵自己的数据.类是数据和操作的集合.类的函数被称为成员函数

(forc++),或method(forsmalltalkanddelphi).类的数据被称为成员变量(c++)orproperty(Delphi).Classis抽象的数据结构.

对象是类的变量,它是运行时的单位。

可以说类时一种概念,而对象时概念具象化的实体。如同狗和一只狗的关系提示(比较structure和variableofstructure.Structure是一种类型,variableofstructure是运行时可访问的一块内存区).英文部分时C++和Delphi关于面向对象方面的相关介绍。可以作为比对和参照。如果学习过C++可忽略该部分。如果对对象构造和析构有疑惑可参见下面65页

Nowweseetheformalgrammarofobject-orientedprogramming.InC++languageC++supportsmultiplestyleprogramming,includingproceduralprogramming,object-orientedprogramming,genericprogramming,object-basedprogramming.

declarationofclass

class

ClassName{privatedatadeclarationprivatememberfunctionsdeclaration

public:datadeclaration;memberfunctionsdeclaration};TheclassisakeywordthatindicatestheClassNameisatypeofclass.Thepublicisakeywordthatshowsthedataorfunctionsbelowitcanbeaccessedbyother(thescopeofaccesswillbediscussedinnextsection).Bydefaultthedataandmemberfunctionsareprivate,whichmeanstheycannotbeaccessfromoutside,onlybeaccessedbymemberfunctionoftheclass(subclasscan’taccessalso.)

classPerson{floatheight;floatweight;char*ID;public:

Person(char*aID);

~Person();voidsetHeight(floatvalue);floatgetHeight();voidsetWeight(floatvalue);floatgetWeight();char*getID();};Thedeclarationofmemberdataandfunctionisthesameasproceduralprogramming.Theclasshastwopairsoffunctions,oneset/getheightmembervariable,theotherset/getweightmembervariable.ThenameoftheclassisPerson.

constructoranddestructorfunctions:TherearetwospecialfunctionsofclassPerson:

Person(char*aID);~Person();Theirnamearethesameasclassname.

Person(char*aID);isconstructorfunction.~Person();isdestructorfunction.Whatareconstructoranddestructorfunction?Nowletusereturntotheclassconceptionthatweunderstandinformersection.Whatisaclass?likeastructure,butassociatedwithsomefunctions.Whatisanobject?Instructureofmemoryitisavariableofstructure.Howtoallocatememoryforavariableofstructure?(1)ifthevariableislocalorglobal,

gi;//globalvariable,automaticallyallocatedondatasegment.

voidp(){

inti;//localvariable,automaticallyallocatedonstack.}(2)ifvariableisapointer,weneedallocateitonheapwithmallocfunction.

int*pi;pi=(int*)malloc(sizeof(int));//allocatedonheap.Nowthatobjectisavariableofstructureatnature.Itfaceshowtoallocatememory.Therearetwocasesforitalso.(1)localvariableorglobalvariableautomaticallyallocatedonstackordatasegment.PersongPerson;//globalvariable,automaticallyallocatedon//datasegment.voidp(){PersonaPerson;//localvariable,automaticallyallocatedon//stack(2)variableisapointerofaclass

(2)variableisapointerofaclassPerson*aPerson;//Classislikestructureso,ithaspointeralsoHowtoallocatememoryforpointeraPerson?Itismalloc?Orotherconvenientway?Waitformoment,let’sreturntotheproblem,whatisconstructoranddestructorofaclass?Nowthatobjectisallocatedonstack,datasegmentorheap.Wemayneedwritesomemembervariableofanobjectwhenobjectiscreating.Iflookanobjectasaproduct,we

温馨提示

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

评论

0/150

提交评论