`
ynp127lu
  • 浏览: 13430 次
最近访客 更多访客>>
社区版块
存档分类
最新评论

delphi笔记2

 
阅读更多

delphi笔记2
2011年06月16日
  一个类型声明指定一个标志符,来表示一种数据类型。类型声明的语法为
  type newTypeName = type
  这里,newTypeName 是一个有效的标志符。比如,给定如下的类型声明
  type TMyString = string;
  你就可以声明变量
  var S: TMyString;
  同时声明多个变量时不能包括初始化,Variant 和文件类型的变量声明也不能初始化。
  如果你没有明确地初始化一个全局变量,编译器把它初始化为0。相反,不能在声明局部变量时进行初
  始化,它们的值是随机的,直到赋给它们一个值。
  你可以创建一个新变量,它和另一个变量在内存的同一个位置。要这样做的话,声明这个新变量时在类
  型名的后面跟关键字absolute,后面再跟一个已存在(先前声明)的变量。比如,
  var
  Str: string[32];
  StrLen: Byte absolute Str;
  指定变量StrLen 从Str 的地址开始。因为短字符串的第一个字节包含字符串的长度,StrLen 的值就是Str
  的长度。
  使用absolute 声明时不能初始化变量,也不能组合其它指示字(和absolute 一同使用)。
  可以调用GetMem 或New 过程来创建动态变量,这种变量在堆中分配内存,它们不能自动管理。
  使用FreeMem 来释放由GetMem 创建的变量,使用Dispose 释放由New 创建的变量。
  其它能作用于动态变量的标准例程包括ReallocMem、Initialize、StrAlloc 和StrDispose。
  声明线程局部变量时,使用threadvar,而不是var,比如,
  threadvar X: Integer;
  线程变量声明
  • 不能出现在过程或函数中
  • 不能包含初始化
  • 不能指定absolute 指示字
  不能创建指针或过程类型的线程变量,也不能在动态调入库中使用线程变量(除了包)。
  由编译器管理的动态变量,即长字符串、宽字符串、动态数组、Variants 和接口,能被声明为threadvar,
  但编译器不能自动释放由每个线程创建的堆内存。若使用这些类型的线程变量,要负责释放它们的内存。
  资源字符串的声明像真常量,除了用resourcestring 代替const。表达式等号的右边必须是常量表达式并
  且返回一个字符串。
  在默认的{$J-}编译状态下,类型常量不能被赋予新值,实际上,它们是只读变量;但如果使用了{$J+}
  编译器指示字,类型常量能被赋予新值,它们在本质上就像初始化的变量。
  要声明数组常量,把数组元素的值用括号括起来,值之间用逗号隔开,这些值必须是常量表达式。比如,
  const Digits: array[0..9] of Char = (’0’, ’1’, ’2’, ’3’, ’4’, ’5’, ’6’, ’7’, ’8’, ’9’);
  上面的声明可以方便地表示为
  const Digits: array[0..9] of Char = ’0123456789’;
  要声明一个记录常量,在括号中使用fieldName: value 的形式来指定每个字段的值,每个字段用分号隔开。
  每个字段的值必须是常量表达式。字段列出的顺序必须和声明的相同,若有tag 字段,则必须指定它的
  值;若记录有一个Variant 部分,只有tag 字段选定的Variant 才能被赋值。
  举例如下:
  type
  TPoint = record
  X, Y: Single;
  end;
  TVector = array[0..1] of TPoint;
  TMonth = (Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec);
  TDate = record
  D: 1..31;
  M: TMonth;
  Y: 1900..1999;
  end;
  const
  Origin: TPoint = (X: 0.0; Y: 0.0);
  Line: TVector = ((X: -3.1; Y: 1.5), (X: 5.8; Y: 3.0));
  SomeDay: TDate = (D: 2; M: Dec; Y: 1960);
  记录常量不能包含文件类型的值。
  过程和函数统称为例程(routine)
  标准过程Exit 可出现在任何过程或函数中,它结束例程的执行,并立即把程序控制返回到例程调用的地方。
  一个过程声明有如下格式:
  procedure procedureName(parameterList); directives;
  localDeclarations;
  begin
  statements
  end;
  函数声明和过程声明类似,除了它要指定一个返回值的类似和返回值。函数声明有如下格式:
  function functionName(parameterList): returnType; directives;
  localDeclarations;
  begin
  statements
  end;
  函数名本身也扮演一个特殊的变量,它和内置的变量Result 一样,存储函数的返回值。
  Result 和函数名并不是能完全互换的,当函数名出现在赋值语句的左边时,编译器假设它
  用来跟踪(存储)返回值(就像Result);在任何其它情况下,编译器把它解释为对它的递归调用。而对
  Result,它可以作为变量用在运算、类型转换、集合构造器、索引以及调用其它例程。
  只要启用了扩展语法({$X+}),Result 在每个函数中被隐含声明,不要试图重新声明它。
  若还没有给Result 或函数名赋值,程序就结束了,则函数的返回值没有被定义(undefined)。
  在声明过程或函数时,你可以使用下面的指示字之一来指明调用约定:register、pascal、cdecl、stdcall
  以及safecall。比如,
  function MyFunction(X, Y: Real): Real; cdecl;
  ...
  调用约定决定了参数被传递给例程的顺序,它们也影响从堆栈中删除参数、传递参数时寄存器的使用,
  以及错误和异常处理。默认的调用约定是register。
  • register 和pascal 调用从左到右传递参数,也就是说,最左边的参数最早被计算并传递,最右边的
  参数最后被计算和传递;cdecl、stdcall 和safecall 调用从右到左传递参数;
  • 除了cdecl 调用,过程和函数在返回之前从堆栈中移除参数,而使用cdecl,当调用返回时,调用者
  从堆栈中移除参数;
  • register 调用能使用多达3 个CPU 寄存器传递参数,而其它调用则全部使用堆栈传递参数;
  • safecall 调用实现了异常“防火墙”,在Windows 下,它实现了进程间COM 错误通知。
  默认的register 调用是最有效的,因为它通常避免了要创建堆栈结构(stack frame)(访问公布属性的方
  法必须使用register);当调用来自C/C++编写的共享库中的函数时,cdecl 是有用的;通常,当调用外部
  代码时,推荐使用stdcall 和safecall。在Windows 中,系统API 使用stdcall 和safecall,其它操作系统
  通常使用cdecl(注意,stdcall 比cdecl 更有效)。声明双重接口的方法必须使用safecall;保留pascal 调用是为了向后兼容性。
  指示字near、far 和export 用在16 位Windows 编程中,它们对32 位程序没有影响,保留它们是为了向后兼容性。
  在声明过程或函数时,用forward 指示字取代例程块(包括局部变量声明和语句),比如,
  function Calculate(X, Y: Integer): Real; forward;
  forward 声明的目的是把过程或函数标志符的作用域提前,这允许在它被实际定义之前,其它过程和函
  数可以进行调用。除了能使你更灵活地组织代码外,forward 声明对相互递归调用(mutual recursion)有
  时是必须的。
  在声明过程或函数时,用external 指示字取代例程块,能允许你调用和程序分开编译的例程。外部例程
  可以来自目标文件或动态调入库(dynamically loadable library)。
  当导入一个带有可变数目参数的C++函数时,要使用varargs 指示字。比如,
  function printf(Format: PChar): Integer; cdecl; varargs;
  varargs 指示字只能用于外部例程,并且只能使用cdecl 调用约定。
  要调用目标文件中的例程,首先要使用$L(或$LINK)编译器指示字把目标文件链接到你的程序中。比
  如,
  在Windows 下: {$L BLOCK.OBJ}
  在Linux 下: {$L block.o}
  把BLOCK.OBJ(Windows)或block.o (Linux)链接到程序或它所在的单元。然后,声明你想调用的
  函数和过程:
  procedure MoveWord(var Source, Dest; Count: Integer); external;
  procedure FillWord(var Dest; Data: Integer; Count: Integer); external;
  现在,你就能调用来自BLOCK.OBJ 或block.o 的MoveWord 和FillWord 例程了。
  像上面的声明,经常用来访问由汇编语言编写的外部例程,你也可以直接在Object Pascal 源代码中放置
  汇编语言写的例程。
  要从一个动态调入库(.so 或.DLL)导入例程,把如下格式的指示字
  external stringConstant;
  放在一个正常的过程头或函数头的尾部。这里,stringConstant 是用单引号括起来的库文件的名称。比如,
  在Windwos 下
  function SomeFunction(S: string): string; external ’strlib.dll’;
  从strlib.dll 导入一个叫做SomeFunction 的函数。
  在Linux 下,
  function SomeFunction(S: string): string; external ’strlib.so’;
  从strlib.so 导入一个叫做SomeFunction 的函数。
  在导入例程时,它的名称可以和库中的名称不同。如果你这样做,在external 指示字中指定它的原始名
  称。
  external stringConstant1 name stringConstant2;
  这里,第一个stringConstant 给出了库文件的名称,第二个stringConstant 是例程的原始名称。
  在Windows 下:比如,下面的声明从user32.dll(Windows API 的一部分)导入一个函数。
  function MessageBox(HWnd: Integer; Text, Caption: PChar; Flags: Integer): Integer;
  stdcall; external ’user32.dll’ name ’MessageBoxA’;
  函数的原始名称是MessageBoxA,但导入后的名称是MessageBox。
  你可以使用一个数字代替名称,来指定你要导入的例程:
  external stringConstant index integerConstant;
  这里,integerConstant 是输出表(export table)中例程的索引。
  在Linux 下:比如,下面的声明从libc.so.6 导入一个标准系统函数。
  function OpenFile(const PathName: PChar; Flags: Integer): Integer; cdecl;
  external ’libc.so.6’ name ’open’;
  函数的原始名称是open,但导入后的名称是OpenFile。
  在你的导入声明中,要保证例程的名称没有拼写错误,并且大小写一致。但在以后调用这些例程时,它
  们是不区分大小写的。
  重载例程必须使用overload 指示字,并且它们有不同的参数列表。
  当重载例程被声明为forward、或在单元的接口部分声明时,在它的定义声明部分必须重新列出它的参数。
  参数以下面几种方式进行分类:
  • 每个参数分为value(数值参数)、variable(变量参数)、constant(常量参数)或out(out 参数),
  默认是数值参数。关键字var、const 以及out 分别表示变量参数、常量参数和out 参数。
  • 数值参数总是有类型的,而常量参数、变量参数和out 参数既可以是有类型的,也可以是无类型的。
  • 数组参数有特殊规则。
  文件类型以及包含文件的结构类型(的实例)只能作为变量参数传递。
  数值参数通过数值传递,而变量参数通过引用传递。
  如果例程声明了一个var 参数,你必须给它传递一个能被赋值的表达式,也就是一个变量、类型化常量
  (typed constant,在{$J+}状态下)、dereferenced 指针、字段或者索引变量(indexed variable)
  当使用out 参数时,传给例程的引用参数的初始值被忽略。out 参数只是为了输出,也就是说,它告诉
  函数或过程在哪里存储输出,但不提供任何输入。
  out 参数经常用在分布式对象模型中,比如COM 和CORBA。而且,当向函数或过程传递未初始化的变
  量时,你应当使用out 参数。
  特殊标志符OpenString 能用于声明可变长度的短字符串参数:
  procedure Check(S: OpenString);
  当编译器指示字{$H-}和{$P+}都起作用时,在声明参数时关键字string 等同于OpenString。
  短字符串、OpenString、$H 和$P 是为了向后兼容性。在新代码中,使用长字符串来避免这种情况
  开放数组参数遵循下列规则:
  • 元素的下标总是从0 开始,第一个是0,第二个是1,依此类推。标准函数Low 和High 返回0 和
  Length-1。SizeOf 函数返回传给例程的实际数组的大小;
  • 它们只能通过元素进行访问,不允许给整个开放数组赋值;
  • 它们只能被当作开放数组参数或无类型var 参数传给其它过程和函数,它们不能传给SetLength 函
  数;
  • 你可以传递一个变量而不是数组,变量的类型就是开放数组的基础类型,它被当作一个长度为1 的
  数组。
  Variant 开放数组参数允许你向一个过程或函数传递由不同类型的元素构成的数组。要定义这样一个例
  程,指定array of const 作为参数的类型,这样
  procedure DoSomething(A: array of const);
  声明了一个叫做DoSomething 的过程,它能接收不同类型的数组。
  array of const 结构等同于array of TVarRec。TVarRec 在System 单元定义,表示一个拥有变体部分的记
  录,它能存储整数、布尔、字符、实数、字符串、指针、类、类引用、接口和变体类型的值。TVarRec
  记录的VType 字段指示数组中每个元素的类型。一些类型以指针而不是以数值形式进行传递,特别是,
  长字符串以指针类型传递,必须被转换为string。
  有默认值的参数必须出现在参数列表的最后
  在过程类型中指定的默认值会覆盖实际例程中指定的默认值。所以,给出下面的声明
  type TResizer = function(X: Real; Y: Real = 1.0): Real;
  function Resizer(X: Real; Y: Real = 2.0): Real;
  var
  F: TResizer;
  N: Real;
  语句
  F := Resizer;
  F(N);
  导致(N, 1.0)传给Resizer。
  默认参数局限于能被常量表达式所表示的值,所以,动态数组、过程、类、类引用或者接口类型的参数
  除了nil 外不能给它们指定默认值,而记录、变体、文件、静态数组和对象类型则根本不能指定默认值。
  若在重载例程中使用默认参数,要避免引起歧义。
  开放数组构造器和集合构造器类似,是由逗号隔开的表达式序列,并且被一对中括号包围。
  开放数组构造器只能当作数值参数或常量参数传递。构造器中的表达式必须和数组参数的基础类型是赋
  值兼容的。对于Variant 开放数组参数,表达式可以是不同的类型。
  一个类声明有如下格式
  type className = class (ancestorClass)
  memberList
  end;
  在类声明中,方法看起来就像函数(或过程)头,而没有函数(或过程)体。方法的定义出现在程序的
  其它地方。比如,这里是Classes 单元中TMemoryStream 类的声明
  type
  TMemoryStream = class(TCustomMemoryStream)
  private
  FCapacity: Longint;
  procedure SetCapacity(NewCapacity: Longint);
  protected
  function Realloc(var NewCapacity: Longint): Pointer; virtual;
  property Capacity: Longint read FCapacity write SetCapacity;
  public
  destructor Destroy; override;
  procedure Clear;
  procedure LoadFromStream(Stream: TStream);
  procedure LoadFromFile(const FileName: string);
  procedure SetSize(NewSize: Longint); override;
  function Write(const Buffer; Count: Longint): Longint; override;
  end;
  除了TObject,System 单元还声明了一个类引用类型TClass。
  类和它的祖先类是赋值兼容的,所以,某个类类型的变量能引用它的任何子类类型的实例。
  除了类类型,你可以使用如下语法声明一个object 类型
  type objectTypeName = object (ancestorObjectType)
  memberList
  end;
  Object 类型不能有published 成员。因为object 类型不是从TObject 继承,它们没有内置的构造函数和析构函数,也没有其它方法。
  你能使用New 过程创建Object 类型的实例,并使用Dispose 过程销毁它们,你也可以像使用记录一样,采用简
  单方式声明object 类型的变量。Object 类型只是为了向后兼容性,不推荐使用它们。
  类的每个成员都有一个称为可见性的属性,用下面的关键字之一来表示它:private、protected、public、published 和automated。
  private表示最小程度的访问能力,protected 表示中等程度的访问能力,public、published 和automated 表示最大程度的访问能力。
  若声明一个成员时没有指定其可见性,则它和前面的成员拥有相同的可见性;若在类声明的开始没有指
  定可见性,当在{$M+}状态下编译类时(或者继承自一个在{$M+}状态下编译的类),它的默认可见性是
  published,否则,它的可见性是public。
  通过重新声明,你可以在派生类中增大一个成员的可见性,但你不能降低它的可见性。比如,一个protected
  属性在派生类中能被改变为public,但不能改为private。还有,published 成员在子类中不能改为public。
  若声明一个类时以class 和分号结束,也就是有下面的格式,
  type className = class;
  在class 后面没有列出父类,也没有成员列表,这是一个forward 声明。Forward 声明的类必须在同一个
  声明区域进行定义声明,换句话说,在forward 声明和它的定义声明之间除了类型声明外,不能有任何
  其它内容。
  虽然类声明既可以出现在单元的interface 部分,也可以出现在implementation 部分,但类方法的实现(定
  义声明)必须出现在implementation 部分。
  。指示字应当只出现在类声明中,并且以下面的顺序列出:
  reintroduce; overload; binding; calling convention; abstract; warning
  这里,binding 是virtual、dynamic 或override;calling convention 是register、pascal、cdecl、stdcall 或
  safecall;warning 是platform、deprecated 或library。
  关键字inherited 在实现多态行为时扮演着特殊角色,它出现在方法定义中,后面跟一个标志符或者不跟。
  方法分为静态方法(默认)、虚方法和动态方法。虚方法和动态方法能被覆盖,它们可是是抽象的。
  方法默认是静态的。
  要实现虚方法或动态方法,在声明时包含virtual 或dynamic 指示字。不像静态方法,虚方法和动态方
  法能在派生类中被覆盖。当调用一个被覆盖的方法时,类或对象的实际类型决定了哪种实现被调用(运
  行时),而不是它们被声明的类型。
  要覆盖一个方法,使用override 指示字重新声明它就可以了。声明被覆盖的方法时,它的参数的类型和
  顺序以及返回值(若有的话)必须和祖先类相同。
  只有虚方法和动态方法能被覆盖,但是,所有方法都能被重载
  虚方法和动态方法在语义上是相同的,唯一的不同是在运行时决定方法调用的实现方式上,虚方法在速
  度上进行了优化,而动态方法在代码大小上做了优化。
  在声明方法时,如果它和继承的方法具有相同的名称和参数,但不包含override,则新方法仅仅是隐藏
  了继承下来的方法,并没有覆盖它。这样,两个方法在派生类中都存在,方法名是静态绑定的。
  reintroduce 指示字告诉编译器,当隐藏一个先前声明的虚方法时,不给出警告信息。比如,
  procedure DoSomething; reintroduce; // 父类也有一个DoSomething 方法
  当要使用新方法隐藏继承下来的虚方法时,使用reintroduce 指示字。
  抽象方法是虚方法或动态方法,并且在声明它的类中没有实现,而是由它的派生类来实现。声明抽象方
  法时,必须在virtual 或dynamic 后面使用abstract 指示字。比如,
  procedure DoSomething; virtual; abstract;
  只有当抽象方法在一个类中被覆盖时,你才能使用这个类或它的实例进行调用。
  一个方法可以使用overload 指示字来重新声明,此时,若重新声明的方法和祖先类的方法具有不同的参
  数,它只是重载了这个方法,并没有隐藏它。当在派生类中调用此方法时,依靠参数来决定到底调用哪
  一个。
  若要重载一个虚方法,在派生类中重新声明时使用reintroduce 指示字。
  在一个类中,你不能以相同的名字公布(published)多个重载的方法,维护RTTI 信息要求每一个公布
  的成员具有不同的名字。
  作为属性读写限定符的方法不能被重载。
  实现重载的方法时,必须重复列出类声明时方法的参数列表。
  按惯例,构造函数通常命名为Create
  constructor Create;
  constructor Create(AOwner: TComponent);
  Message 方法用来响应动态分派的消息。Message 方法在各个平台上都是支持的,VCL 使用message 方
  法来响应Windows 消息,CLX 不使用message 方法来响应系统事件。
  比如,在Windows 下:
  type
  TTextBox = class(TCustomControl)
  private
  procedure WMChar(var Message: TWMChar); message WM_CHAR;
  ...
  end;
  消息处理函数很少直接调用,相反,消息是通过继承自TObject 的Dispatch 方法来分派给对象的。
  procedure Dispatch(var Message);
  传给Dispatch 的参数Message 必须是一个记录,并且它的第一个字段是Cardinal 类型,用来存储消息号
  码。
  Dispatch 按类的层次结构向后搜索(从调用对象所属的类开始),它将调用和传给它的消息具有相同号码
  的message 方法。若没有发现指定号码的message 方法,Dispatch 调用DefaultHandler。
  比如,给定下面的声明
  property Color: TColor read GetColor write SetColor;
  GetColor 方法必须被声明为:
  function GetColor: TColor;
  SetColor 方法必须被声明为下面之一:
  procedure SetColor(value: TColor);
  procedure SetColor(const value: TColor);
  (当然,SetColor 的参数名不必非得是value。)
  数组属性是被索引的属性,它们能表示像下面的一些事物:列表中的条目、一个控件的子控件和位图中
  的象素等等。
  声明数组属性时包含一个参数列表,它指定索引的名称和类型,比如,
  property Objects[Index: Integer]: TObject read GetObject write SetObject;
  property Pixels[X, Y: Integer]: TColor read GetPixel write SetPixel;
  property values[const Name: string]: string read Getvalue write Setvalue;
  定义数组属性时可以在后面使用default 指示字,此时,数组属性变成类的默认属性。
  若一个类有默认属性,你能使用缩写词object[index]来访问这个属性,它就相当于object.property[index]。
  索引限定符能使几个属性共用同一个访问方法来表示不同的值。
  若一个属性有索引限定符,它的读写限定符必须是方法而不能是字段。
  可选指示字stored、default 和nodefault 被称为存储限定符,它们对程序的行为没有影响,但决定了RTTI
  的维护方式,它们决定是否把公布属性的值存储到窗体文件中。
  stored 指示字后面必须跟True、False、Boolean 类型的字段名或者一个返回Boolean 值的无参数方法。
  比如,
  property Name: TComponentName read FName write SetName stored False;
  若一个属性没有stored 指示字,就相当于指定了stored True。
  default 指示字后面必须跟随一个和属性具有相同类型的常量,比如,
  property Tag: Longint read FTag write FTag default 0;
  要覆盖一个继承下来的默认值而不指定新值,使用nodefault 指示字。default 和nodefault 只支持有序类
  型和集合类型(当它的基础类型是有序类型,并且上下边界都在0 到31 之间时)。若声明属性时没有使
  用default 或者nodefault,它被当作nodefault 看待。对于实数、指针和字符串,它们分别有隐含的默认
  值0、nil 和 ’ ’(空串)
  声明时没有指定类型的属性称为属性覆盖,它允许你改变一个属性继承下来的可见性或限定符。
  类引用类型有时称为元类,用如下的构造形式表示  class of type
  每个类从TObject 继承了两个分别叫做ClassType 和ClassParent 的方法,前者返回对象的类引用,后者
  返回对象的父类类引用。这两个方法的返回值都是TClass(这里TClass = class of TObject)类型,它们
  能被转换为更加明确的类型。每个类还继承了一个叫做InheritsFrom 的方法,它测试调用的对象是否从
  一个指定的类派生而来(如果对象是类的一个实例,结果如何?)。这些方法被is 和as 运算符使用,很
  少直接调用它们。
  is 运算符执行动态类型检查,用来验证运行时一个对象的实际类型。
  object is class
  as 运算符执行受检查的类型转换。表达式
  object as class
  类方法是作用在类而不是对象上面的方法(不同于构造函数)。类方法的定义必须以关键字class 开始,
  比如,
  type
  TFigure = class
  public
  class function Supports(Operation: string): Boolean; virtual;
  class procedure GetInfo(var Info: TFigureInfo); virtual;
  ...
  end;
  类方法的定义部分也必须以class 开始,比如,
  class procedure TFigure.GetInfo(var Info: TFigureInfo);
  begin
  ...
  end;
  类方法类似C++/C#里的static成员????
  类方法既可以通过类引用来调用,也可以使用对象,当使用后者时, Self 值等于对象所属的类。
  要创建一个异常对象,在raise 语句中调用异常类的构造函数。比如,
  raise EMathError.Create;
  通常,raise 语句的格式是
  raise object at address
  这里,object 和at address 都是可选的。
  异常在try...except 语句中被处理,比如,
  try
  X := Y/Z;
  except
  on EZeroDivide do HandleZeroDivide;
  end;
  SysUtils 单元声明了几个标准例程来处理异常,它们包括ExceptObject、ExceptAddr 以及ShowException。
  日期:2004年3月17日 星期三  天气:晴朗  作者:Napoleo
  Delphi语法笔记3
  发表:2004-3-17 15:36:54  出处:你的博客网(yourblog.org)
  --------------------------------------------------------------------------------
  在使用文件变量前,必须调用AssignFile 过程把它和一个外部文件相关联。外部文件通常是一个命名的
  磁盘文件,但它也可以是一个设备,比如键盘或显示器。
  一个已存在的文件可使用Reset 过程打开,使用Rewrite 过程能创建一个新文件并打开它。使用Reset 打开的
  文本文件是只读的,使用Rewrite 和Append 打开的文本文件只能写入。对类型文件和无类型文件,不管用Reset 还是用
  Rewrite 打开,它们都是可读写的。
  对类型文件和无类型文件,能使用Seek 进行随机访问
  标准函数FilePos 和FileSize 能用来确定当前文件位置和当前文件大小。
  当程序完成一个文件的处理时,必须使用CloseFile 关闭文件。
  每行以一个Eoln 标志(一个回车符,或许还跟一个换行符)结束
  有两个标准文本文件变量,Input 和Output。Input 是一个只读文件,和操作系统的标准输入(通常是键
  盘)相关联。Output 是只写文件,和操作系统的标准输出(通常是显示器)相关联。在程序执行前,Input
  和Output 自动打开,
  无类型文件使用关键字file 声明,没有其它内容。
  var Datafile: file;
  代替Read 和Write,BlockRead 和BlockWrite 两个过程用于高速数据传输。
  System 单元提供了三个函数,WideCharToString、WideCharLenToString 和StringToWideChar,它们用来
  把0 结束宽字符串转换为单字节或双字节长字符串。
  动态调入库(dynamically loadable library)在Windows 下是一个动态链接库(dynamic-link library,DLL),
  在Linux 下是一个共享目标库(shared object library)
  导入过程或函数最简单的方法是用external 指示字声明它们,比如,
  在Windows 下: procedure DoSomething; external ’MYLIB.DLL’;
  在Linux 下: procedure DoSomething; external ’mylib.so’;
  你可以直接调用操作系统的库函数来访问一个库中的例程,这些库函数包括LoadLibrary、FreeLibrary 和
  GetProcAddress。在Windows 下,这些函数在Windows.pas 单元声明,在Linux 下,为了兼容性考虑,
  它们在SysUtils.pas 单元实现,实际的Linux 例程是dlopen、dlclose 和dlsym(这些都在Kylix 的Libc 单
  元声明)。此时,我们使用过程类型的变量来引用导入的例程。
  当一个例程在exports 子句中列出时,它将被输出,它的格式如下
  exports entry1, ..., entryn;
  只有在Windows 下能使用索引说明符,它包括指示字index,后面跟一个介于1 到2,147,483,647 之间的
  数字常量(为提高程序效率,使用较小的索引值)。若入口中没有指定索引,在输出表中例程被自动赋予
  一个号码。
  名称说明符包括指示字name,后面跟一个字符串常量。若入口没有名称说明符,例程被输出时使用声
  明的原始名称,包括拼写和大小写。当要使用不同的名称输出一个例程时,使用name 子句。比如,
  exports
  DoSomethingABC name ’DoSomething’;
  当在动态调入库中输出重载的函数或过程时,你必须在exports 子句中指定它的参数列表
  exports 子句可出现在程序或库声明部分的任何位置,次数也不受限制,同样,当出现在单元的接口或实
  现部分时,情况也是如此。程序很少包含exports 子句。
  一个库的块(block)所包含的语句构成了库的初始化代码,每当库被调入时,这些代码执行一次。它们
  的典型任务包括注册窗口类和初始化变量等。库的初始化代码也可以使用ExitProc 变量安装一个退出过
  程(exit procedure),就像在Exit procedures 中描述的那样。退出过程在库被卸载时执行。
  库的初始化代码通过设定ExitCode 变量为非0 来标记一个错误。ExitCode 在System 单元声明,默认值
  时0。若库的初始化代码把ExitCode 设置为其它值,库将被卸载,调用程序被通知发生了错误。类似地,
  若初始化代码执行中发生了未处理的异常,调用程序也将被通知调入库时失败。
  在共享库中声明的全局变量不能被Object Pascal 程序导入。
  使用IsLibrary 变量来确定代码是作为程序还
  是库执行,IsLibrary 在程序中总是True,在库中总是False。在库的生命期内,HInstance 存储了它的
  实例句柄,CmdLine 在库中总是nil。
  DLLProc 变量允许一个库监测操作系统对它的入口点(entry point)的调用,这个特征通常只是由支持多
  线程的库使用。DLLProc 在Windows 和Linux 下都存在,但用起来不同。在Windows 下,DLLProc 用
  于多线程程序,在Linux 下,它用来判断库何时被卸载。对所有的退出行为,你应该使用finalization
  sections,而不是退出过程。
  在Windows 下,若DLL 输出的例程以长字符串或动态数组作为参数或者作为函数的返回值(不管是直
  接的,还是通过记录或对象封装的),那么,DLL 和它的客户程序(或DLL)必须使用ShareMem 单元;
  当一个程序或DLL 调用New 或GetMem 分配内存,而在另一个模块中调用Dispose 或FreeMem 来释放
  内存时,上面的规则同样适用。ShareMem 单元应当在程序或库的uses 子句中第一个列出。
  ShareMem 是BORLANDMM.DLL 内存管理器的接口单元,它允许在模块间共享动态分配的内存。
  BORLANDMM.DLL必须连同使用ShareMem单元的程序和DLL一同发布。当程序或DLL使用ShareMem
  时,它的内存管理器被BORLANDMM.DLL 中的取代。
  Linux 使用glibc 的malloc 来管理共享内存。
  为了区分包和其它库,包被存储在文件
  • 在Windows 下,包的扩展名是.bpl(Borland package library)
  • 在Linux 下,包通常以前缀bpl 开始,扩展名是.so。
  通常,程序启动时包被静态调入,但你可以使用LoadPackage 和UnloadPackage 例程(在SysUtils 单元)
  来动态调入包。
  包源文件不包括类型、数据、过程或函数声明。取而代之的是,它包含
  • 包的名称;
  • 它所需要的其它包的列表。这些包被链接到新包中;
  • 包被编译时所包含的(或绑定的)单元文件列表。包实际上是这些代码单元的一个外包装,这些单
  元为编译后的包提供功能。
  包的声明有如下形式:
  package packageName;
  requiresClause;
  containsClause;
  end.
  package DATAX;
  requires
  baseclx,
  visualclx;
  contains Db, DBLocal, DBXpress, ... ;
  end.
  在Windows 下,一个程序的堆栈由两个值定义:堆栈的最小值和最大值。这两个值受编译器指示字
  $MINSTACKSIZE 和 $MAXSTACKSIZE 所控制,它们的缺省值分别是16,384(16K)和1,048,576
  (1M)。在Linux 下,堆栈大小只能由环境设置。
  在register 约定下,最多有3 个参数可通过CPU 寄存器传递,其余(若有的话)参数被传递到栈。此时,
  参数以声明的顺序(和pascal 相同)被传递,前3 个有资格的参数分别使用EAX、EDX 和ECX 寄存器。
  过程和函数必须保留EBX、ESI、EDI 和EBP 寄存器,但可以修改EAX、EDX 和ECX。当在汇编语言
  中实现构造和销毁时,保证预留DL 寄存器。过程和函数被调用时,是假定CPU 的direction 标志是清除
  的(对应于CLD 指令),并且返回时,direction 标志也必须是清除的。
  以下约定适用于函数的返回值:
  􀁺 可能的话,有序类型通过寄存器返回值:字节通过AL 返回,字通过AX 返回,双字通过EAX 返
  回。
  􀁺 实数类型的返回值在浮点协处理器的栈顶寄存器(top-of-stack register,ST(0))。对于Currency 类
  型的返回值,ST(0)中的值被乘以10000。比如,Currency 值1.234 在ST(0)中的值为12340。
  􀁺 对字符串、动态数组、方法指针、Variant、或Int64 类型的返回值,就像函数在其它参数的后面额
  外声明了一个var 参数。换句话说,是函数调用者传递一个额外的32 位指针,它指向的变量用来
  返回结果。
  􀁺 指针、类、类引用和过程指针类型,结果通过EAX 返回。
  􀁺 对静态数组、记录和集合类型,若结果占用1 个字节,它通过AL 返回;若结果占用2 个字节,
  它通过AX 返回;若结果占用4 个字节,它通过EAX 返回。否则(结果超过4 个字节),结果通
  过一个额外的var 参数返回,它在所有声明的参数的后边。
  在register 调用约定下,Self 就像在所有其它参数的前面声明,所以,它总是通过EAX 寄存器传递。
  在pascal 调用约定下,Self 就像在所有其它参数的后面声明(有时还要包括返回函数值的额外的var 参
  数),所以,它最后被压入栈,所在的地址比其它参数要低。
  像类一样,接口只能在程序或单元的最外层声明,而不能在过程或函数中声明。一个接口类型的声明有
  如下格式
  type interfaceName = interface (ancestorInterface)
  [’{GUID}’]
  memberList
  end;
  • memberList 只包括方法和属性,字段在接口中是不允许的;
  • 因为接口没有字段,所以属性的读(read)和写(write)限定符必须是方法;
  • 接口的所有成员都是公有的(public),不允许使用可见性限定符和存储限定符(但一个数组属性能
  被声明为default);
  • 接口没有构造函数和析构函数,它们不能被(直接)实例化,除非使用实现了它们(的方法)的类;
  • 方法不能被声明为virtual、dynamic、abstract 或override。因为接口自己不实现它们的方法,这些
  声明没有意义。
  在某些接口声明中,interface 关键字被换成了dispinterface,这种构造(连同dispid、readonly 和writeonly
  指示字)是平台相关的,不能在Linux 程序中使用。
  声明一个接口时可以指定一个祖先接口,如果没有指明的话,则它直接继承自IInterface。IInterface 在
  System 单元定义,是其它所有接口的根类。IInterface 定义了三个方法:QueryInterface、_AddRef 和
  _Release。
  注意:IInterface 和IUnknown 是相同的。考虑到平台无关性,通常要使用IInterface;IUnknown 最好用
  在一些特殊的程序中,它依赖于Windows 平台。
  默认的调用约定是register,但当接口在程序模块(尤其当它们用其它语言编写时)间共享时,需要声
  明所有的方法为stdcall 调用方式;实现CORBA 接口时使用safecall 调用约定;在Windows 下,你可以
  用safecall 来实现双重调度接口的方法。
  接口声明的属性只能通过接口类型的表达式进行访问,类类型的变量不行;并且,接口的属性只在接口
  被编译的程序中是可见的。比如,在Windows 下,COM 对象没有属性。
  在接口中,属性的读和写必须通过方法来完成,因为不存在字段。
  因为每个接口都继承自IInterface,所以,一个实现接口的类必须实现QueryInterface、_AddRef 和
  _Release 方法。System 单元中的TInterfacedObject 实现了这些方法,所以,其它实现接口的类可以方便
  地通过继承它来实现。
  implements 指示字允许你在实现类中委托一个属性来实现接口,比如
  property MyInterface: IMyInterface read FMyInterface implements IMyInterface;
  上面声明了一个叫做MyInterface 的属性,它实现了接口IMyInterface。
  在属性声明中,implements 指示字必须是最后一项,它可以实现多个接口,接口之间以逗号分隔。
  实现委托接口的类应当从TAggregatedObject 派生。
  如果委托的属性是接口类型,那么此接口(或者它的派生接口)必须出现在类声明中的祖先列表中(也
  就是声明实现这些接口)。委托的属性必须返回一个对象,此对象所属的类完全实现了implements 所指
  明的接口,并且没有使用方法解析子句。
  如果委托属性是一个类类型,那么在定位实现的方法时,会先搜索这个类以及它的祖先类,然后再搜索
  当前类(也就是定义属性的类)以及它的祖先类。所以,可以在属性指定的类中实现某些方法,而另一
  些方法在当前类实现。可以象平常一样使用方法解析子句来避免含糊的声明,或者(只是)使用一个特
  别的方法声明。一个接口不能委托给多个类类型的属性实现。
  如果你声明一个接口类型的变量,则它可以引用任何实现这个接口的类实例。这样的变量使你可以调用
  接口的方法,而不必在编译时知道接口是在哪里实现的。但要注意以下限制:
  • 使用接口类型的表达式只能访问接口定义的方法和属性,不能访问实现类的其它成员;
  • 一个接口类型的表达式不能引用实现了它的派生接口的类实例,除非这个类(或它继承的类)还明
  确实现了此祖先接口。
  接口引用通过引用计数进行管理,它依赖于从IInterface 继承的_AddRef 和_Release 方法。若一个对象只
  通过接口来引用,我们没必要手动销毁它,当它最后的引用超出范围时,它会自动销毁。
  全局类型的接口变量能只被初始化为nil。
  要判断一个接口类型的表达式是否引用了一个对象,通过标准函数Assigned 来完成。
  一个类和它实现的任何接口是赋值兼容的,一个接口和它的任何祖先接口是赋值兼容的。nil 可以被赋给
  任何接口类型的变量。
  一个接口类型的表达式可以被赋予一个变体类型(Variant):若接口类型是IDispatch 或它的后代,则
  Variant 变量的类型码是varDispatch,否则为varUnknown。
  类型码为varEmpty、varUnknown 或者varDispatch 的Variant 变量,可以赋给IInterface 类型的变量;类
  型码为varEmpty 或varDispatch 的Variant 变量,可以赋给IDispatch 类型的变量。
  若对象所属的类实现了IDispatch 接口(在System 单元声明),则此对象是一个自动化对象。自动化对象
  只适用于Windows。
  派遣接口类型定义了一个自动化对象的方法和属性,它们通过IDispatch 接口来实现。调用派遣接口的方
  法是通过在运行时调用IDispatch 接口的Invoke 方法来实现的,a class cannot implement a dispatch
  interface。
  派遣接口声明具有如下格式:
  type interfaceName = dispinterface
  [’{GUID}’]
  memberList
  end;
  除了dispid,在派遣接口中声明的方法不能使用其它指示字,它的参数以及返回值必须属于自动化类型,
  也就是说,必须是Byte、Currency、Real、Double、Longint、Integer、Single、Smallint、AnsiString、WideString
  双重接口既支持编译时绑定,也支持通过自动化动态绑定(运行时)。双重接口必须从IDispatch 接口派生。
  双重接口的所有方法(除了从IInterface 和IDispatch 继承的方法)必须使用safecall 调用约定,并且方
  法的参数和返回值必须是自动化类型。(自动化类型包括Byte、Currency、Real、Double、Real48、Integer、
  Single、Smallint、AnsiString、TdateTime、Variant、OleVariant 和WordBool)
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics