从Turbo Pascal到Delphi
● 牢骚
我是在高一接触pascal语言,因为参加NOI的需要,顺理成章的要使用Turbo Pascal来写程序了。半年后,我开始想着如何编写Windows程序,又理所当然的找上Delphi。初见Delphi,除了begin,end让我觉得倍感亲切外,Object Pascal里的增加的面向对象的语法却让我很是吃惊,当时的我可根本不懂什么叫面向过程,面向对象;最可恶的是,国内那些教育家们,除了会拿着清华的那本精简的不能再精简的pascal教材照本宣科外,似乎再也没有什么实质性的工作了,传说中的《Turbo Pascal大全》更是无处可寻,所以关于unit,interface这些Delphi里随处可见的关键字我也很不明白。所幸,其后不久,我得到一本名为《计算机反病毒技术》的书,里面统统都是用Turbo Pascal编写的源代码,通过它我迅速明白了早已存在于Turbo Pascal中unit,interface等关键字的含义和用法,又以Delphi中的Help文档为扶手,开始蹒跚学步了。
印象中,国内Delphi作家似乎更偏爱编写应用实例类的技术书籍,至于语法这种东西,没有几个人愿意多去涉及,即使书中必须谈及,也是寥寥数笔,匆匆带过,或者干脆与某本书类似。对Object Pascal语法讲解最好,最权威的恐怕就算《Delphi5开发人员指南》了,这本书至今也是备受推崇的。但与如今泛滥的C++书籍相比,Delphi仍然逊色许多,也难怪很多新手特别是从来没有接触过pascal语言的新手,在学习Object Pascal时会遇到不少困难。自己的感觉是:在从Turbo Pascal向Delphi过渡的过程中,由于没有正确的指引,走了很多弯路;由于没有正确的桥梁,必须要一步实现大跨越。所以,在这里,我提出自己曾经遇见的沟壑,路标性给出我自己的认识和总结,希望给入门的同学们一些帮助。我不打算详细介绍语法知识,并假设你已经有一点pascal语言和面向对象概念的基础。要想学习相关详细知识,我推荐各位一定要阅读《Delphi开发人员指南》和Delphi Help文档中的相关章节。
● Record,Class,Object
习惯了在一个Program模块内写完所有面向过程代码的我,有几天的时间一直未能彻底明白在非Unit模块中,自定义类的框架,语法是如何的,VCL源代码虽然经典,却过于繁杂,不能让我迅速掌握根本,我需要一个最简单又最能说明问题,完整的可运行的代码,苦于无处寻求答案,只好亲自动手,探索对应关系,终成其下两段代码。
program TP;
{本代码在Turbo Pascal 7.0下编译通过}
type
MyRecord = record
{...}
end;
var
MR: MyRecord;
procedure MyProcedure;
begin
{MyProcedure Code}
end;
{=========== main ===========}
begin
{以这个begin为标志,主程序开始,其作用相当于C/C++中的main函数}
MyProcedure;
end.
上面是一段极其简单的包含记录类型声明和过程声明的代码,二者基本规则如下:用户自定义的数据类型,需要放在以保留字“type”开头的代码段中;过程(procedure)和函数(function)要放在以保留字“var”开头的代码段中;最后一个夹在begin和end间的代码段是主程序的开始,也就是整个程序的入口,作用相当于C/C++里的main函数,请注意,只有在以program保留字开头的代码模块中,这个begin和end才具有程序入口的作用。
再看下面的代码:
program
{代码在Delphi7.0下编译通过}
{$APPTYPE CONSOLE}
uses
SysUtils;
type
TMyClass = class(TObject)
public
constructor Create;
procedure PrintClassName;
private
ClassName: string;
end;
var
MyClass: TMyClass;
constructor TMyClass.Create;
begin
ClassName := 'TMyClass';
end;
procedure TMyClass.PrintClassName;
begin
writeln(ClassName);
end;
{=========== main ===========}
begin
MyClass := TMyClass.Create;
MyClass.PrintClassName;
MyClass.Free;
readln;
end.
类作为用户自定义的一种数据类型,其声明的规则,成员函数、过程的实现方法都符合经典 Pascal的基本规则,唯一不同的是保留字变了,从记录体变成了类(详细比较代码结构和语法规则),这也说明Object Pascal是在经典Pascal的基础上进行了面向对象内容的语法扩充。当然,内部的运行机制并没有表面语法扩充这么轻松,可那是编译器的事情,在这里,我们完全不用理会。差点忘记告诉读者怎么调试上面的代码了:在IDE环境主菜单里选择 File | New | Other ,在New Item项里选择Console Application,这时出现了代码编辑框,再将上面的代码贴入,F9,完成!
代码内的{$APPTYPE CONSOLE}是一个编译开关,它告诉编译器,这是控制台程序,在格式上它与注释的差别就是那个“$”符号;TMyClass = class(TObject)可以简写为
TMyClass = class,表示TMyClass类从TObject类继承而来,TObject是Delpi中所有类的祖先,这也是为什么我在代码中没有声明Destroy过程却仍然能够使用的原因;Delphi中类的构造很有趣,请注意MyClass := TMyClass.Create这一句,这与C++不同[注1]。readln使程序停顿下来,直到用户按下回车键才结束程序退出。更多详细内容请参考《Delphi开发人员指南》
上面两段代码相互对应,虽然很简单,不过我却认为他们在某种程度上很容易让同学发现由经典Pascal向Object Pascal过渡的一些方法,对Object Pascal的类定义语法有个初步了解,这是很重要的一步。当初我要是能够看到这两段代码,或许能少浪费很多时间了。
另外,在TP5.5 –Delphi 7.0中都支持Object类型(对象类型)。语法如下:
object
Field;
Field;
...
Method;
Method;
end;
Method允许以下几种形式:
procedure MethodName(<parameter(s)>:type);
或者function MethodName(<parameter(s)>:type):type;
或者constructor MethodName(<parameter(s)>: type [;<parameter(s)>:type]); [virtual];
或者destructor MethodName[(<parameters>: type)];[virtual];
不错的,构造函数和析构函数都支持virtual,在构造函数中,还有一个有用的东西是Fail函数,当构造函数的初始化失败时,它可以用来释放已经分配的资源。
接下来的代码,是Turbo Pascal的Help文档中,关于Fail函数的演示代码,可以让大家对此有个较深的认识。Turbo Pascal确实是很强大和优秀的。
type
PBase = ^TBase;
TBase = object(TObject) {在这里就已经出现Tobject了,是不是很亲切?}
constructor Init(FailMe: Boolean);
end;
PDerived = ^TDerived;
TDerived = object(TBase)
constructor Init(FailMe: Boolean);
end;
constructor TBase.Init(FailMe: Boolean);
begin
inherited Init;
if FailMe then Fail;
end;
constructor TDerived.Init(FailMe: Boolean);
begin
if not inherited Init(FailMe) then
{判断父类的初始化是否成功}
{ Ancestor failed to construct, we must fail too }
Fail;
{ Otherwise, proceed with construction }
{...}
end;
var
P: PObject;
X: Boolean;
begin
for X := False to True do
begin
P := New(PDerived, Init(X));
if P <> nil then
begin
writeln('Object constructed sucessfully');
Dispose(P, Done);
end
else
writeln('Object failed to construct');
end;
end.
自定义的Object不一定要从Tobject继承下来,因此它也没有内建的构造、析构和其他方法,通常使用New过程和Dispose过程建立和销毁它的实例。Delphi中仍旧支持Object,并有property成员了,但是仍然不允许published。KOL[注2]也使用该类型实现自己的构架。但是对此关键字的保留主要是为了兼容,建议用户改用class关键字。
● unit模块
在Turbo Pascal的Help文档里,是这样说明unit功能的:Units are the basis of modular programming inBorland Pascal. You use units to createlibraries and to divide large programs into logically related modules。小规模的程序,我们可能将所有代码集中在一个program中,可是面对更加复杂的大型工程时,正确的划分功能封装功能对代码管理和以后的维护有着重要的作用,而使用unit模块正好解决了这些问题。其语法规则如下:
unit identifier; { Heading }
interface { Public symbols }:
uses { Uses clause }
const { Constants }
type { Types }
var { Variables }
procedure { Procedures }
function { Functions }
implementation { Private symbols }:
uses { Uses clause }
label { Labels }
const { Constants }
type { Types }
var { Variables }
procedure { Procedures}
function { Functions }
begin { Initialization }
statement; { Statements }
statement
end.
Interface部分用来声明对外接口,也就是可以被外部引用该文件的程序使用的函数和过程;implementation部分包含接口部分声明的各种函数、过程具体实现的代码;begin一直到最后的end.之间都是初始化部分,可以为本unit内的各种变量,过程,函数初始化。如果没有内容需要初始化,那么begin可以省略,但end.必须存在。
在Delphi下经典Pascal中的unit部分有了变动,请看来源于Delphi Help文档的说明:
unit Unit1;
interface
uses
{ List of units goes here }
{ Interface section goes here }
implementation
uses
{ List of units goes here }
{ Implementation section goes here }
initialization
{ Initialization section goes here }
finalization
{ Finalization section goes here }
end.
可见Initialization部分的开始关键字begin,被Initialization取代了,并且增加了一个finalization部分。Initialization部分的代码可以这样写:
initialization
begin
{do something…}
end;
也可以这样写:
initialization
{do something…}
finalization部分的功能,有点类似于析构函数,它主要针对本unit模块中initialization部分初始化的资源进行释放,并且是在程序结束时运行,如果程序以Halt过程结束了,该部分的程序将不能执行。
Unit模块中的interface等关键字和结构初看似乎有些限制程序员的自由度,但也正是这种语法规定体现出Pascal语言的严谨和优美,为减少程序出错的几率做出保证。
program相当文章的提纲挈领,unit则是文章的各个段落。Delphi里,program模块包含在.prj文件中,unit模块包含在传统的.pas文件中。这就是为什么在Delphi中我们经常面对的是为各个窗口服务的unit模块,而非在一个program中写完所有代码。不过我在Delphi的Help中看到这样一句话:In traditional Pascal programming, all source code, including the main program, is stored in .pas files.不知道这算思考角度不同还是算bug,毕竟这种语法并非Delphi中才有,Turbo Pascal程序员也一直在采用这种方法组织程序结构。
给出一个简单的initialization例子。在菜单中选择File | New | Application,再在窗体上放置一个按钮,双击该按钮编写它的Click事件处理代码,完整代码如下:
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
Msg: string;
implementation
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
begin
ShowMessage(Msg);
Msg := 'second';
end;
initialization
Msg := 'first';
end.
以上所述,是过渡中两个基本的重要问题,弄懂它们方可初步明白自己为什么要这样编写代码,该在哪里编写代码,如何扩展代码功能。
● 我推荐的参考书籍
Delphi参考书籍
《Delphi 5 开发人员指南》机械工业出版社
《Inside VCL》李维 电子工业出版社
《Delphi深度历险》陈宽达 科学出版社
《Pascal精要》网络下载电子版
Windows参考书籍
《Programming Windows》Charles Petzold
《Windows2000开发人员指南》中国水利水电出版社
Delphi组件参考书我暂时没有发现特别好的,平时我主要依靠论坛,源代码,Delphi自带的Demo和文档来学习组件的使用。
● 再说点
现在有一些大学取消了Pascal语言课程,去年的ACM大赛也取消了Pascal语言的使用,不禁心寒,启蒙教育没有人做了,这些都使得Delphi在学生中的处境更加艰难。在国内的Delphi论坛上,常常见到许多半吊子程序员在享受Delphi的快速开发的时候,嘴巴里还在责备Delphi功能太弱,不能搞什么底层开发,甚至直接责怪Pascal语言,殊不知,在Dos年代,有多少著名软件使用Pascal开发出来的呢,有多少底层控制程序有着Pascal的身影呢?现在我手头上还有Pascal编写的病毒代码,反病毒代码,IC芯片控制代码。
或许正是Delphi的RAD能力降低了程序开发的门槛,让很多半吊子进入了程序界,RAD开发蒙蔽了许多半吊子编程者(称呼他们为程序员或许稍欠火候)的眼睛,但是国内的教育界同样有着不可推卸的责任,选修课开VB的不少,讲Delphi的很少,我曾经还遇到过一个从心底里就瞧不起Delphi,不啻