delphi中的难点.doc_第1页
delphi中的难点.doc_第2页
delphi中的难点.doc_第3页
delphi中的难点.doc_第4页
delphi中的难点.doc_第5页
已阅读5页,还剩6页未读 继续免费阅读

下载本文档

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

文档简介

Self一,什么是self:简单的可以理解为代表所编的程序范围内的一个类或组件。最根本的解释是self是一个指针变量,代表当前正在执行函数的那个对象的数据块的首地址。简单的说就是在函数或过程的声明中(如Procedure Tform1.Buttonclick(Sender:Tobject); )可看出self是代表哪个组件(即Tform1),即self代表.号之前的组件。二,什么地方用self:只能用在类方法中(即含有create的程序段中),而不能用在过程或函数中,类方法是指给具体可执行的公式进行运算的一段程序,是正在按操作者的要求正在进行某项数值运算或操作的那段程序,是必需要内存中进行动态变化的一个过程,是活的。而过程和函数则是只定义了一些公式的一段程序,是存储在硬盘中的一些公式,是死的。在delphi中要实现一个类方法,要经过下面五个步骤:1。定义一个类及其所包含的方法,属性;type tcou=class procedureAdd(n:integer)end;2。用该类声明一个名字(实质是一个指针)var a:tcou;3。用该类实例化一个对象(即create),并使它与先前的名字联系起来a:=TCou.create; 为什么不是a.Create呢?那是因为,在执行这条代码前,还没有为a分配内存空间,是不能访问他内部的数据成员的。4。调用对象的方法或属性a.add(5);5。释放对象a.free;form1:=tform.create(application); 当应用程序关闭时form1会被释放form1:=tform.create(self); 当前窗体释放时form1会被释放form1:=tform.create(nil); 需要自己动手释放,即后面要写tform.freeSender1, 什么是sender:简单的理解是Sender是用来表示程序中的控件名。深入的理解是正在运行的程序中哪个控件接收这个事件并调用相应的处理过程2.Sender的用法:使用Sender参数可以通过单一过程段处理多类控件.由于Sender代表了调用所在过程的控件,那么你就可以直接把它拿来当那个控件名用,不过如果要用属性的话,最好写成(Sender as 控件名).控件属性:=. 例如:procedure TForm1.Edit1Click(Sender: TObject);beginwith Sender as TEdit do/蓝色代码相当于(sender as Tedit do).texe:=”hello”begintext:=hello;end;end;.如果在两个事件中处理同样的事情,那么可以利用Sender来省去重写同样的过程。例如:Procedure TForm1.Button1Click(Sender:TObject);begindo same sth.;if Sender=Button1 thendo sth.;if Sender=Button2 thendo other sth.;end;procedure TForm1.Button2Click(Sender:TOBJect);beginButton1Click(Button2);end;你可以编写一个单一的事件处理句柄,通过Sender参数和IFTHEN语句或者CASE语句配合,来处理多个构件。发生事件的构件或控件的值已经赋给了Sender参数,该参数的用途之一就在于:可以使用保留字IS来测试Sender,以便找到调用这个事件处理句柄的构件或控件的类型。例如,将表单中编辑框和标签的Click事件的处理句柄都指向表单的xxx过程,编辑框和标签对Click事件有不同的反应:procedure TForm1 xxx(Sender:TObject);beginif(sender is Tedit) thenshowmessage(this is a editbox);if(sender is Tlabel) thenshowmessage(this is a label);end;Sender参数的第二个用途是结合AS操作符进行类型转换,将若干个派生于某一父类的子类强制转换成该父类。例如表单中有一个TEdit类控件和一个TMemo控件,它们实际上都派生于TcustomEdit类,如果你要为二者的某一事件提供同样处理,可以将二者事件句柄都指向自定义的过程yyy:Procedure TForm1.yyy(Sender:TObject);begin(sender as TcustomEdit).text:=This is some demo text;end;在过程中,AS操作符将TEdit类和TMemo类均强制转换成TcustomEdit类,再对TcustomEdit类的属性赋值。注意这种转换必须符合Delphi中类的层次关系。Owner和parentOwner代表的是容纳当前控件的最初的控件是哪个,最初的窗体的所有者是 Applicationparent代表的是容纳当前控件的最亲近控件是哪个。如在form1上建个panel1,然后在panel1上再建个按钮button1,那么panel1和button1的owner都是form1,form1的owner是application,而panel1的parent是form1,button1的parent是panel1.常用语句是Button.parent=panel1;/在panel1上建个button控件 Button.parent:=self;Height与Clientheight,Width与ClientwidthHeight就是Clientheight,Width就是Clientwidth,而对于窗体而言,Height是包括标题条在内的高度,而Clientheight是指窗体工作区的高度。同理,Clientwidth是指定窗体工作区的宽度。 Delphi 的函数覆盖(Override)与重载(overload)什么是override:在子类中定义某方法与其父类有相同的名称和参数,为了让方法中可以编写一个与父类不同的计算方程式而采用。如父类的方法中做的是加法运算,而在子类中我们想做乘法运算,这就要重新书写运算方程式,我们说该方法被重写 (Overriding)。子类的对象使用这个方法时,将调用子类中的定义,对它而言,父类中的定义如同被“屏蔽”了。什么是overload:在一个类中定义了多个同名的方法,它们或有不同的参数个数或有不同的参数类型,则称为方法的重载(Overloading)。Overloaded的方法是可以改变返回值的类型。而Delphi 就是用override 关键字来说明函数覆盖的。被覆盖的函数必须是虚(virtual)的,或者是动态(dynamic)的,也就是说该函数在声明时应该包含这两个指示字中的一个,比如:procedure Draw; virtual;在需要覆盖的时候,只需要在子类中用override 指示字重新声明一下就可以了。procedure Draw; override;首先,我想单独说明一下overload,为什么呢?因为overload和对象化关联不大,所以,我感觉单独提出来说明比较好。 我们都知道,在Pascal语法规则中,同一个UNIT里是不能存在两个同名的函数的,例如: 代码: function func(): Boolean; function func(const x: Char): Boolean;这样是会出语法错误的,原因是因为标识符规则限制。但是问题出来了,如果我们需要几个功能相似但是参数不同的函数,那么根据标识符规则,我们就必须定义几个不同名,但是功能相同或相似的函数。 如,假设我需要一个函数,要实现返回参数一减去参数二后的值,那么我们就将这样定义函数: 代码: function SubInt(const Value1, Value2: Integer): Integer; functino SubReal(const Value1, Value2: Real): Real; function SubDouble(const Value1, Value2: Double): Double; implementation function SubInt(const Value1, Value2: Integer): Integer; begin Result:= Value1 - Value2; end; functino SubReal(const Value1, Value2: Real): Real; begin Result:= Value1 - Value2; end; function SubDouble(const Value1, Value2: Double): Double; begin Result:= Value1 - Value2; end;我们可以看到,这三个函数除了参数名不同外,实现部分是一样的,这为程序的一致性带来了一定的麻烦。如果能定义一个同名函数来处理这几种不同情况的话,便有利于代码的重用,也有利于我们的记忆。这时我们就可以用overload来完成我们的需求了。 代码: function Sub(const Value1, Value2: Integer): Integer; overload; function Sub(const Value1, Value2: Real): Real; overload; function Sub(const Value1, Value2: Double): Double; overload; implementation function Sub(const Value1, Value2: Integer): Integer; begin Result:= Value1 - Value2; end; function Sub(const Value1, Value2: Real): Real; begin Result:= Value1 - Value2; end; function Sub(const Value1, Value2: Double): Double; begin Result:= Value1 - Value2; end;由此可见,名字相同而参数的数据类型或数量不同的函数,称之为overload函数。这是因为编译器在编译时不仅使用了函数名的信息,还使用了参数类型和数量的信息,通过组合这些信息可以确定分配地址时需要的唯一标识,通常我们叫他signature。例如: 代码: Sub(1, 2); / 调用的是 function Sub(const Value1, Value2: Integer): Integer; Sub(1.0, 2.0); / 调用的是 function Sub(const Value1, Value2: Real): Real;这里要说明一下,如果是同名函数,并且参数也相同,只是返回值不同的话,是不能实现overload的。 对于overload的说明就到这里吧,下面我们来说override: 前面我说过,在派生一个新类的时候,首先会继承父类中的所有可继承部分,那么就意味着,默认情况下,父类所有的非私有 部分的类成员或方法都已经包含在新类中。 但是,如果我们要为类的一个方法添加新的代码而又不希望重写父类方法中原有功能的话,我们可以用override来实现方法的覆盖,并用inherited来继承(或者可以说调用)父类方法中的方法功能: 代码: type TBaseClass = class(TObject) / 定义一个新的父类 protected FMessage: string; public constructor Create(); / 构造方法 procedure MessageBox(); virtual; / 显示字符串 end; TChildClass = class(TBaseClass) / 定义一个TBaseClass的派生类 public procedure MessageBox(); override; / 重载父类中的方法 end; implementation constructor TBaseClass.Create; begin inherited Create(); / 继承父类中的构造方法Create FMessage:= ClassName; / 初始化信息字符串为类名 end; procedure TBaseClass.MessageBox; begin ShowMessage(FMessage); / 显示字符串 end; procedure TChildClass.MessageBox; begin FMessage:= FMessage + FMessage; inherited MessageBox(); / 继承父类中原有MessageBox方法的原有功能 end;好,我们可以编写一个简单的例子来调用一下: 代码: var Child: TChildClass; begin Child:= TChildClass.Create; Child.MessageBox(); Child.Free; end;代码执行后,应该可以看到一个弹出“TChildClassTChildClass”字符串的对话框。 以上代码只是简单的演示了类方法的继承,也许你会奇怪,为什么TBaseClass的构造方法Create没有用override关键字指定,却同样可以inherited来继承父类的该方法,其实不用override也是可以实现继承的(在这里如果你加上override反而会出语法错误,因为TObject类的Create是一个静态方法,而静态方法是不允许被override的),那么他们有什么不一样的呢?我们来看下面的例子: 代码: var Child: TBaseClass; begin Child:= TChildClass.Create; Child.MessageBox(); Child.Free; end;执行这段测试代码和我们上面的代码是一样的效果的。然后我们再把TBaseClass类的方法MessageBox后的virtual关键字和子类中的override关键字去掉,再执行一次看看,应该是看到一个弹出“TChildClass”字符串的对话框。 这是为什么呢?原因是因为virtual关键字,下面简单的说明一下virtual关键字: virtual和dynamic方法 virtual关键字是指定方法为虚方法,Object Pascal中,默认情况下,所有方法都是静态的(当然和类方法不同),静态方法的特点是,在对象被创建的时候,编译器会分配所有调用静态方法时所指定的内存地址,也就是说,编译期即指定了调用的具体方法。 而virtual和dynamic方法(虚方法和动态方法)则是在调用时动态分配的。他们的语义是相同的,唯一不同的是,他们的实现方法和调用方法,这涉及到DELPHI的编译机制。 Delphi的编译器会自动维护用于添加虚方法的虚方法表(VMT)和用于添加动态方法的动态方法表(DMT)。 虚方法表存放的是类及其基类声明的所有虚方法指针。每一个类都有并且只有一个虚方法表,当然每一个虚方法表都有一个入口。无论一个类是否有他自己定义的虚方法,只要他继承了祖先类的虚方法,他也就会有自己的虚方法表,并列举出他继承的所有虚方法。因为每一个类都有一个自己的虚方法表,Delphi就可以使用虚方法表来识别一个类,实际上,一个类引用就是指向类的虚方法表的指针。 由于基类和派生类的虚方法表之间有过强的关联性,因此派生类继承了祖先类的虚方法,也就是说派生类的虚方法表的前面一部分必须与基类相同。而当基类和派生类不在同一个DLL或EXE中的时候,这个要求是很难满足的。基类一旦改变,派生类如果不重新编译,将导致错误。另外,如果类的继承很深,那么虚方法表将会因不断继承的虚方法而膨胀,导致效率低下。 而动态方法则可以解决这个问题,每一个类的动态方法表中只维护部分的动态方法,也就是说动态方法表只列出了该类所声明的动态方法,他并不包括从祖先类那里继承来的动态方法。动态方法表使用了一种独特的编号和检索系统,当调用动态方法时,会在动态方法表中的Indexes数组中查找该方法的代号,一旦找到匹配该方法的代号,则方法指针会到相应的地址中调用方法。如果没有找到,则会搜索祖先类中的动态方法表。这样虽然解决了虚方法表膨胀的问题,但是产生了执行效率的问题。 总的来说,虚方法侧重于提高速度,动态方法侧重于节省内存,如果某方法其后代类基本都需要override的,那么推荐使用virtual,如果某方法不经常或者几乎不在后代类中被override的,则推荐使用dynamic。 abstract方法 abstract方法就是我们常说的抽象方法,该方法只能有声明而不能有实现部分,一般只是提供给派生类override使用的,所以一般abstract关键字和virtual是一起出现的。而一般含有抽象方法的类一般我们叫他虚类。 类方法 类方法和C中的static方法有点相似,Object Pascal中的类方法是不需要实例化对象就可以调用的方法。由于他不需要为对象分配内存空间就可以调用他,那么也就意味着在类方法中不能引用类中的其他数据成员。 类方法的声名如下: 代码: type TNewClass = class(TObject) public class procedure AClassFunciton(.): .; end; inherited就是调用祖先类的函数,如果不带参数就是默认调用同名函数如果带参数则表明子类中的函数个数可能比祖先类要多取其中的几个参数传过去例如祖先类有个函数 Create(AName:string);子类有个函数 Create(AName:string;AComponent:TObject);override;那么子类的Create函数内就可以这样调用祖先类:procedure TAClass.Create(AName:string;AComponent:TObject);begin Inherited Create(AName);end;unitUnit1;interfaceusesWindows,Messages,SysUtils,Variants,Classes,Graphics,Controls,Forms,Dialogs,StdCtrls;typeTForm1=class(TForm)Button1:TButton;Button2:TButton;Button3:TButton;procedureButton1Click(Sender:TObject);procedureButton2Click(Sender:TObject);procedureButton3Click(Sender:TObject);privatePrivatedeclarationspublicPublicdeclarationsend;THuman=class/基类publicprocedureshowinfo1;virtual;procedureshowinfo2;virtual;end;TStudent=class(Thuman)/派生类publicprocedureshowinfo1;override;/虚拟继承procedureshowinfo2;override;end;TWorker=Class(THuman)/派生类publicprocedureshowinfo1;override;procedureshowinfo2;override;end;varForm1:TForm1;implementation$R*.dfmprocedureTHuman.showinfo1;beginShowMessage(THumanShowinfo1);end;procedureTHuman.showinfo2;beginShowMessage(THumanShowinfo2);end;procedureTStudent.showinfo1;beginShowMessage(TStudentShowinfo1);inheritedshowinfo2;/调用父类中不同名的成员函数,即showinfo2,注意本过程是showinfo1, end;procedureTStudent.showinfo2;beginShowMessage(TStudentShowinfo2);end;procedureTWorker.showinfo1;beginShowMessage(TWorkerShowinfo1);inherited;/调用父类中的同名函数,即showinfo1end;procedureTWorker.showinfo2;beginShowMessage(TWorkerShowinfo2);end;procedureTForm1.Button1Click(Sender:TObject);varH:THuman;beginH:=THuman.Create;h.showinfo1;h.showinfo2;end;procedureTForm1.Button2Click(Sender:TObject);varS:TStudent;begins:=TStudent.Create;s.showinfo1;s.showinfo2;end;procedureTForm1.Button3Click(Sender:TObject);varW:TWorker;beginw:=Tworker.Create;w.showinfo1;w.showinfo2;end;functionCreateThread(lpThreadAttributes:Pointer;安全设置一般都是置为 nil, 这表示没有访问限制; dwStackSize:DWORD;堆栈大小这个值都是 0, 这表示使用系统默认的大小, 默认和主线程栈的大小一样, 如果不够用会自动增长;lpStartAddress:TFNThreadStartRoutine;入口函数,即线程名lpParameter:Pointer;函数参数是个无类型指针(Pointer), 用它可以指定任何数据;dwCreationFlags:DWORD;启动选项,有两个可选值: 0: 线程建立后立即执行入口函数; CREATE_SUSPENDED: 线程建立后会挂起等待.varlpThreadId:DWORD输出线程ID,ID也叫线程标识符,直接输入ID两字母,是用来让函数自己调用需要的信息的。ID需要在调用前用var定义为Dword类型,即var ID:DWORD):THandle;stdcall;返回线程句柄例1:hThread:=CreateThread(nil,0,MyThreadFun,nil,CREATE_SUSPENDED,ID);例2:hThread:=CreateThread(nil,0,MyThreadFun,pt,0,ID);句柄:句柄是一个用来表示对象等事物的变量。delphi中我们创建一个VCL控件时,有多种参数:From.create(参数)参数有以下几种:Application:你的控件的父级是主程序,所以他的级别最高.Self:你的控件的父级是当前窗口,他关闭你也关闭.Sender:你的控件的父级是当前对象.Handle:你的控件的父级是此句柄.Nil:无定义.生成的独立窗体需代码释放。一般说来,Delphi类的构造方法在TC

温馨提示

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

评论

0/150

提交评论