分享
 
 
 

深入Delphi(二) 个人认为不错的delphi概要介绍,言简意明,值得一看

王朝delphi·作者佚名  2006-01-08
窄屏简体版  字體: |||超大  

深入Delphi (二)

单元文件/编译器条件标识符

by machine

对比起工程文件,单元文件明显多了一点东西:

unit Unit1;

interface

uses

Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs;

type

TForm1 = class(TForm)

private

{ 私有声明 }

public

{ 公有声明 }

end;

var Form1: TForm1;

implementation

{$R *.DFM}

initialization

{ 在这里进行初始化 }

finalization

{ 在这里进行销解化 }

end.

首先是program换成了unit,然后还有interface和implementation两个关键字把整个单元分为了两部分,即接口和实现部分。

接口部分相当于C中的头文件部分,如果一个单元被uses了,那其实只是被包括了接口的部分。在这一部分中可以定义函数原型,但函数(包括类中的函数)的具体实现部分只能出现在implementation部分里面。与C类似,接口部分和实现部分都可以出现uses语句,以实现全局和局部的限制。接口部分的uses必须紧接着interface关键字出现,实现部分的uses也必须紧接着implementation关键字,就像它们是合在一起似的。

至于实现部分,其实如果把工程文件里面的program一行改成implementation,看起来就成了单元里面的实现部分,实际上也是如此,因为首先,工程文件不能被uses,因此工程总是局部的;另外,实现部分里面也可以出现工程中的begin...end.块。

实现部分的begin...end.块实际上就起到了初始化的作用,但这主要是保留了以前Pascal的语法,Delphi建议使用新增的initialization和finalization来初始化和销解化。在实现部分,begin...end.块、初始化块和销解化块都不是必须出现的,但整个单元必须以end.结尾。

关键字initialization标志着初始化块的开始,当一个编译后的工程运行时,每个单元的初始化块被首先执行,至于那个单元首先被初始化,则取决于被uses的顺序,先被uses的就先被初始化。初始化块和begin...end.块不能同时出现,因此,建议使用初始化块,以便也能使用销解化块的功能。

销解化块只能伴随初始化会出现,但初始化块可以单独出现。销解化块在程序结束前被自动执行,其执行顺序与该单元被uses的顺序相反。要注意的是,初始化块可能在完全被执行之前程序就因某种错误而被中止了,但Delphi确保证销解化块被执行,因此销解块中的代码必须考虑未完全初始化的情况。

然后是对uses语句的详细说明。总结前面所说,uses总共可以在三处地方出现。在单元的两处地方的uses语句里,被uses的单元必须是在工程的搜索路径中,而工程文件中的uses语句则可以直接指明文件的路径:

uses

Forms,

Main,

Extra in '..\EXTRA\EXTRA.PAS';

在单元文件中,如果在接口部分引用了另外一个单元,则实现部分也可以调用该单元,但反之在实现部分引用的单元则不能在接口部分被调用。在编程的时候应该尽量在一个单元的实现部分引用其他单元,而减少在接口部分的引用,以避免所谓的“循环引用”问题。比如说,单元A在接口部分引用了单元B,单元B以同样的方法引用了单元C,最后单元C又在接口部分引用了单元A,则编译时会出现循环引用的错误。解决方法是把全局的声明放到集中的单元中,尽量把引用放到实现部分。

有时候会遇到一个问题,如果一个变量,或者常量、函数,又或者是数据类型,总之,一样相同名称的东西同时在两个被引用的单元中,那么调用的时候到底会调用到哪一个呢?答案是uses语句的单元列表中最后一个,而不是首先出现的那个!这是要注意的。如果不能确定将会调用那个版本,还可以在所调用的东西前面加上“单元名称+句号”的前缀,以强制Delphi调用相应的单元版本。

在单元和工程中还有一类用于控制编译器的特殊命令(Compiler Directive)。这种命令包含在大括号或者“(*”和“*)”的组合中,看起来就像注释,但用“$”这个符号开头。比如例子中的$R就是告诉编译器你要包含一些资源文件到工程中。其中有一组命令,$DEFINE、$UNDEF、$IFDEF、$IFNDEF、$ELSE和$ENDIF,看起来很像C中的宏定义命令,但Delphi中的这种定义只起到开关的作用,而不能定义宏替换,也就是说可以定义一个开关名称,使其处于开状态,而如果没有定义这个开关,则认为其处于关状态,但不能像C中那样定义这个开关为一段程序代码,并在编译是自动用代码替换宏,Delphi没有提供这样的宏功能。

要是对编译器指令不了解,那么请继续看下去,否则就可以直接跳到下一章了。好,首先,什么叫做编译呢?编译就是把你编写的源代码,转译为CPU能执行的指令。通常情况下,编译器把每个单元文件单独编译,然后连接器负责把编译后的单元文件连接为可执行文件(exe文件)。之所以要这样做,是因为不同的操作系统,其可执行文件的格式是不一样的,比如DOS、Windows、Linux这些操作系统。但只要CPU是兼容的型号,则可以使用同样的编译文件格式,因为转译过程是一样的。比如C的编译文件OBJ文件,即使是在DOS下编译的,仍然可以被连接为Windows的EXE,编译器只对CPU感兴趣,而对要输出的执行文件的格式不感兴趣。

我个人觉得对要学一样东西如果对其了解得越多,学起来就更能融会贯通,所以有时候可能会扯得很远。还有,要学好一样东西,耐心是必不可少的哦!所以一时弄不明白不要泄气,说不定过一段时间之后就突然明白了呢,我以前也试过这样子的。好了,回到刚才Delphi的DEFINE问题。初学者很容易搞不明白这样的定义和常量/变量声明的作用有什么不同:

{$DEFINE Debug}

begin

{$IFDEF Debug}

Writeln('Debug is on');

{$ENDIF}

end;

然后对比一下这个例子:

const Debug = True;

begin

if Debug then begin

Writeln('Debug is on');

end;

end;

两个例子运行起来结果是一模一样的。但事实上编译出来的程序是不一样的。在第二个例子中,首先,程序被编译,然后运行,CPU首先判断Debug的值是否不为零,如果是,则继续执行调用Writeln,没什么特别之处。但第一个例子的情况就不一样了,在编译时,编译器发现了$DEFINE Debug这条指令,于是它在内部设立标识,记录了Debug这个定义(DEFINE),然后继续编译过程,之后,编译器又发现了$IFDEF Debug这条指令,编译器查看自己的内部标识表,发现Debug是已经定义的,因此它继续进行$IFDEF和$ENDIF之间的源代码的编译,否则的话,它将忽略这段源代码,就好像他们根本不存在的样子!所以,第一段例子编译出来的程序与以下例子的是一模一样的:

begin

Writeln('Debug is on');

end;

这样,源代码在编译的时候编译器就已经作了判断,编译出相应的程序,而不是程序在运行的时候,CPU再做出判断,调用相应的代码。这样一来,程序的运行效率明显提高了,C就是大量使用这种特性的例子,因此C的执行效率比较高,同理,C的编译速度却是非常的慢,因为编译器要维护非常大的标识列表(别忘了C的宏定义比Delphi中的复杂,而且C中到处都是这样的宏定义),而且要不断地搜索列表并根据对应定义来修改程序源代码,最后才能进行CPU指令翻译。以后会讲到如何在Delphi中使用其它语言编写的代码,包括汇编和C等,这样一来,程序的事件处理部分可以用Delphi来完成,而在需求速度的地方可以使用汇编或者C来写,开发速度就能够大大提高,而程序运行效率仍然可以维持在高水平上。

使用DEFINE指令还能做到其他的一些事情,比如向上面的例子那样,定义一个调试(Debug)的编译器条件标识符(Conditional Symbol),然后在代码中插入一些只想在调试时才想执行的代码,比如记录程序每调用一个函数的返回值之类的,用$IFDEF来控制,是非常方便的,如果等到程序要发布,不想要再编译那些调试用的代码,只需注释掉Debug的定义就可以了,而不需要把每个调试用的代码都注释掉。在某些情况下,无法使用Delphi的调试器,比如正在使用Direct Draw,或者调试一个同时被多个程序使用的DLL那样,就只能把要调试的内容记录一个文件中,用查看文件记录的方式来调试,这样子本来就很烦了,如果要维护这样的程序,最好使用DEFINE,否则来回改动程序只会使自己更烦。

$DEFINE的作用还可以继续引申开去,凡是程序有多个代码版本的情况,都可以使用$DEFINE的方便功能。好了,这一章就说到这里吧。请继续留意下一章的出现。

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有