函数重载
重载的思想很简单:编译器允许你用同一名字定义多个函数或过程,只要它们所带的参数不同。实际上,编译器是通过检测参数来确定需要调用的例程。
下面是从VCL 的数学单元(Math Unit)中摘录的一系列函数:
- function Min (A,B: Integer): Integer; overload;
- function Min (A,B: Int64): Int64; overload;
- function Min (A,B: Single): Single; overload;
- function Min (A,B: Double): Double; overload;
- function Min (A,B: Extended): Extended; overload;
当调用方式为Min (10, 20)时,编译器很容易就能判定你调用的是上列第一个函数,因此
返回值也是个整数。
声明重载函数有两条原则:
· 每个例程声明后面必须添加overload 关键字。
· 例程间的参数个数或(和)参数类型必须不同,返回值不能用于区分各例程。
41
下面是ShowMsg 过程的三个重载过程。我已把它们添加到例OverDef 中(一个说明重
载和确省参数的应用程序):
- procedure ShowMsg (str: string); overload;
- begin
- MessageDlg (str, mtInformation, [mbOK], 0);
- end;
- procedure ShowMsg (FormatStr: string;
- Params: array of const); overload;
- begin
- MessageDlg (Format (FormatStr, Params),
- mtInformation, [mbOK], 0);
- end;
- procedure ShowMsg (I: Integer; Str: string); overload;
- begin
- ShowMsg (IntToStr (I) + ' ' + Str);
- end;
三个过程分别用三种不同的方法格式化字符串,然后在信息框中显示字符串。下面是三个例程的调用:
- ShowMsg ('Hello');
- ShowMsg ('Total = %d.', [100]);
- ShowMsg (10, 'MBytes');
当你在例程名后面键入左圆括号时,窗口中会显示所有可用例程的参数列表,当你输入参数时,Delphi会根据所输入参数的类型过滤参数列表。从图6.4 你可看到,当开始输入一个常量字符串时,Delphi 只显示第一个参数为字符串的两个ShowMsg 例程参数列表,滤掉了第一个参数为整数的例程。
重载例程必须用overload 关键字明确标示,你不能在同一单元中重载没有overload 标示
的例程,否则会出现错误信息: "Previous declaration of '<name>' was not marked with
the 'overload' directive."。不过你可以重载在其他单元中声明的例程,这是为了与以前的
Delphi版本兼容,以前的Delphi版本允许不同的单元重用相同的例程名。无论如何,这是例程
重载的特殊情况不是其特殊功能,而且不小心会出现问题。
例如在一个单元中添加以下代码:
- procedure MessageDlg (str: string); overload;
- begin
- Dialogs.MessageDlg (str, mtInformation, [mbOK], 0);
- end;
这段代码并没有真正重载原始的MessageDlg 例程,实际上如果键入:
- MessageDlg ('Hello');
你将得到一个有意思的错误消息,告诉你缺少参数。调用本地例程而不是VCL的唯一途径是明确标示例程所在单元,这有悖于于例程重载的思想:
- OverDefF.MessageDlg ('Hello');
Windows 句柄
Delphi从Windows 引入了不少数据类型,其中句柄最重要。这种数据类型名为THandle,该类型在Windows 单元中定义:
- type
- THandle = LongWord;
句柄数据类型通过数字实现,但并不当数字用。在Windows 中,句柄是一个系统内部数据结构的引用。例如,当你操作一个窗口,或说是一个Delphi 窗体时,系统会给你一个该窗口的句柄,系统会通知你:你正在操作142号窗口,就此,你的应用程序就能要求系统对142 号窗口进行操作——移动窗口、改变窗口大小、把窗口极小化为图标,等等。实际上许多Windows API 函数把句柄作为它的第一个参数,如GDI (图形设备接口)句柄、菜单句柄、实例句柄、位图句柄等等,不仅仅局限于窗口函数,。换句话说,句柄是一种内部代码,通过它能引用受系统控制的特殊元素,如窗口、位图、图标、内存块、光标、字体、菜单等等。Delphi中很少需要直接使用句柄,因为句柄藏在窗体、
位图及其他Delphi 对象的内部。当你要调用Delphi不支持的Windows API 函数时,句柄才会有用。
现在举一个简单的Windows句柄例子,完善这节内容。例WHandle 程序的窗体很简单,
只有一个按钮。正如下面主窗体文本所定义的那样,在代码中添加了窗体的OnCreate 事件和
按钮的OnClick 事件:
- object FormWHandle: TFormWHandle
- 52
- Caption = 'Window Handle'
- OnCreate = FormCreate
- object BtnCallAPI: TButton
- Caption = 'Call API'
- OnClick = BtnCallAPIClick
- end
- end
窗体一创建,程序就会通过窗体本身的Handle 属性,获取窗体对应的窗口句柄。调用IntToStr ,把句柄数值转换为一个字符串,然后再把它添加到窗体标题中,如图9.1:
- procedure TFormWHandle.FormCreate(Sender: TObject);
- begin
- Caption := Caption + ' ' + IntToStr (Handle);
- end;
因为FormCreate 是窗体类的方法,它可直接访问同类的其他属性和方法。因此,在这个过程中我们能够直接访问窗体的Caption 属性和Handle 属性。
如果你多此次执行该程序,通常会获得不同的句柄值。这个值实际上是由Windows 操作系统确定并返回给应用程序的。(句柄从来不是由程序决定的,而且句柄没有预定义值,句柄是由系统决定的,每执行一次程序,产生一个新值。)当你单击按钮,程序将调用Windows API 函数SetWindowText,它会根据第一个传递参
数改变窗口的标题。更准确地说,所用的API 函数其第一个参数是需要修改窗体的句柄:
- procedure TFormWHandle.BtnCallAPIClick(Sender: TObject);
- begin
- SetWindowText (Handle, 'Hi');
- end;
这段代码与前面所讲的事件处理程序等效,它通过给窗体的Caption 属性赋一个新值,改变窗体的标题。对上面这种情况,调用一个API 函数没有什么意义,因为用Delphi 来做更简单。然而有些API 在Delphi中没有相应的函数,就需要直接调用API,这一点你会在后面的高级例子中看到。
回调函数
前面已经了解到Objet Pascal 支持过程类型。过程类型常用于给Windows API函数传递回调函数。
首先,什么是回调函数呢?回调函数就是能对一系列系统内部元素执行给定操作的API函数,例如能对所有同类窗口进行操作的函数。这种函数也叫枚举函数,它是作为参数传递的函数,代表对所有内部元素执行的操作,该函数或过程的类型必须与给定的过程类型兼容。Windows回调函数的应用不止上述一种,不过这里仅研究以上简单应用。
现在考虑 EnumWindows API 函数,它的原型如下(从Win32 帮助文件拷贝而来):
- BOOL EnumWindows(
- WNDENUMPROC lpEnumFunc, // address of callback function
- LPARAM lParam // application-defined value
- );
当然,这是个C 语言的定义。我们可以查看Windows 单元,从中找到相应的Pascal 语言定义:
- function EnumWindows (
- lpEnumFunc: TFNWndEnumProc;
- lParam: LPARAM): BOOL; stdcall;
查阅帮助文件,我们发现作为参数传递的函数应该属于下面的类型(也是在C中):
- BOOL CALLBACK EnumWindowsProc (
- HWND hwnd, // handle of parent window
- LPARAM lParam // application-defined value
- );
这与下面的Delphi 过程类型定义一致:
- type
- 54
- EnumWindowsProc = function (Hwnd: THandle;
- Param: Pointer): Boolean; stdcall;
其中第一个参数是各主窗体的句柄,第二个参数则是调用EnumWindows 函数时所传递的值。实际上,Pascal 中没有相应的TFNWndEnumProc 类型定义 ,它只是个指针。这意味着我们需要传递一个带有合适参数的函数,将它用作一个指针,也就是取函数的地址而不是调用它。不幸的是,这也意味着如果某个参数类型出现错误时,编译器不会给予提示。每当调用Windows API函数或传递一个回调函数给系统时,Windows 要求程序员遵循stdcall调用协定。缺省情况下,Delphi使用另一种更高效的调用协定,其关键字为register。下面是一个与定义兼容的回调函数,此函数把窗口的标题读到字符串中,然后添加到给定窗体的一个列表框中:
- function GetTitle (Hwnd: THandle; Param: Pointer): Boolean; stdcall;
- var
- Text: string;
- begin
- SetLength (Text, 100);
- GetWindowText (Hwnd, PChar (Text), 100);
- FormCallBack.ListBox1.Items.Add (
- IntToStr (Hwnd) + ': ' + Text);
- Result := True;
- end;
窗体有一个几乎覆盖整个窗体的列表框,窗体顶部有一个小面板,面板上有一个按钮。当按下按钮时,EnumWindows API函数被调用,并且GetTitle 函数作为参数传递给它:
- procedure TFormCallback.BtnTitlesClick(Sender: TObject);
- var
- EWProc: EnumWindowsProc;
- begin
- ListBox1.Items.Clear;
- EWProc := GetTitle;
- EnumWindows (@EWProc, 0);
- end;
你可以直接调用GetTitle函数,不必先把值保存到过程类型临时变量中,上例这么做是为了使回调过程更清楚。程序运行结果确实很有意思,正如你在图9.2 中看到的那样,结果显示了系统中正在运行的所有主窗口,其中大部分是隐藏的,你通常看不到,许多实际上没有标题。
http://blog.csdn.net/sushengmiyan/article/details/7453237