上一题下一题
跳转到
 
 
  世界如此多姿,发展如此迅速,窥一斑未必还能知全豹。但正如万花筒一样,每一个管窥都色彩斑斓。  
 
 
  知识通道 | 学习首页 | 教师首页 | PK首页 | 知识创造首页 | 企业首页 | 登录
 
本文对应知识领域
DELPHI语法编程基础(8)
作者:未知 申领版权
2010年12月12日 共有 2208 次访问 【添加到收藏夹】 【我要附加题目
受欢迎度:

    引用参数 
    Pascal 例程的传递参数可以是值参也可以是引用参数。值参传递是缺省的参数传递方式:即将值参的拷贝压入栈中,例程使用、操纵的是栈中的拷贝值,不是原始值。
    当通过引用传递参数时,没有按正常方式把参数值的拷贝压栈(避免拷贝值压栈一般能加快程序执行速度),而是直接引用参数原始值,例程中的代码也同样访问原始值,这样就能在过程或函数中改变参数的值。引用参数用关键字var 标示。
    下面是利用引用传递参数的例子,引用参数用var关键字标示:
    procedure DoubleTheValue (var Value: Integer);
    begin
    Value := Value * 2;
    end;
    在这种情况下,参数既把一个值传递给过程,又把新值返回给调用过程的代码。当你执行完以下代码时:
    var
    X: Integer;
    begin
    X := 10;
    DoubleTheValue (X);
    x变量的值变成了20,因为过程通过引用访问了X的原始存储单元,由此改变了X的初始值。
    通过引用传递参数对有序类型、传统字符串类型及大型记录类型才有意义。实际上Delphi总是通过值来传递对象,因为Delphi对象本身就是引用。因此通过引用传递对象就没什么意义(除了极特殊的情况),因为这样相当于传递一个引用到另一个引用。
    Delphi 长字符串的情况略有不同,长字符串看起来象引用,但是如果你改变了该字符串的串变量,那么这个串在更新前将被拷贝下来。作为值参被传递的长字符串只在内存使用和操作速度方面才象引用,但是如果你改变了字符串的值,初始值将不受影响。相反,如果通过引用传递长字符串,那么串的初始值就可以改变。
    Delphi 3增加了一种新的参数:out。out参数没有初始值,只是用来返回一个值。out参数应只用于COM过程和函数,一般情况下最好使用更有效的var参数。除了没有初始值这一点之外,out参数与var参数相同。
    常量参数 
    除了引用参数外,还有一种参数叫常量参数。由于不允许在例程中给常量参数赋新值,因此编译器能优化常参的传递过程。编译器会选用一种与引用参数相似的方法编译常参(C 术语中的常量引用),但是从表面上看常参又与值参相似,因为常参初始值不受例程的影响。
    事实上,如果编译下面有点可笑的代码,Delphi将出现错误:
    function DoubleTheValue (const Value: Integer): Integer;
    begin
    Value := Value * 2;      // compiler error
    Result := Value;
    end;
    开放数组参数 
    与C语言不同,Pascal 函数及过程的参数个数是预定的。如果参数个数预先没有确定,则需要通过开放数组来实现参数传递。
    一个开放数组参数就是一个固定类型开放数组的元素。 也就是说,参数类型已定义,但是数组中的元素个数是未知数。见下例:
    function Sum (const A: array of Integer): Integer;
    var
    I: Integer;
    begin
    Result := 0;
    for I := Low(A) to High(A) do
    Result := Result   A[I];
    end;
    上面通过High(A)获取数组的大小,注意其中函数返回值 Result的应用, Result用来存储临时值。你可通过一个整数表达式组成的数组来调用该函数:
    X := Sum ([10, Y, 27*I]);
    给定一个整型数组,数组大小任意,你可以直接把它传递给带开放数组参数的例程,此外你也可以通过Slice 函数,只传递数组的一部分元素(传递元素个数由Slice 函数的第二个参数指定)。下面是传递整个数组参数的例子:
    var
    List: array [1..10] of Integer;
    X, I: Integer;
    begin
    // initialize the array
    for I := Low (List) to High (List) do
    List [I] := I * 2;
    // call
    X := Sum (List);
    如果你只传递数组的一部分,可使用Slice 函数,如下:
    X := Sum (Slice (List, 5));
    图 6.1: 单击 Partial Slice 按钮显示的结果 
    
    在Delphi 4中,给定类型的开放数组与动态数组完全兼容。动态数组的语法与开放数组相同,区别在于你可以用诸如array of Integer指令定义变量,而不仅仅是传递参数。
    类型变化的开放数组参数 
    除了类型固定的开放数组外,Delphi 还允许定义类型变化的甚至无类型的开放数组。这种特殊类型的数组元素可随意变化,能很方便地用作传递参数。
    技术上,array of const 类型的数组就能实现把不同类型、不同个数元素组成的数组一下子传递给例程。如下面Format 函数的定义:
    function Format (const Format: string;
    const Args: array of const): string;
    上面第二个参数是个开放数组,该数组元素可随意变化。如你可以按以下方式调用这个函数:
    N := 20;
    S := 'Total:';
    Label1.Caption := Format ('Total: %d', [N]);
    Label2.Caption := Format ('Int: %d, Float: %f', [N, 12.4]);
    Label3.Caption := Format ('%s %d', [S, N * 2]);
    从上可见,传递的参数可以是常量值、变量值或一个表达式。声明这类函数很简单,但是怎样编写函数代码呢?怎样知道参数类型呢?对类型可变的开放数组,其数组元素与TVarRec 类型元素兼容。
    注意:不要把TVarRec 记录类型和Variant 类型使用的TVarData 记录类型相混淆。这两种类型用途不同,而且互不兼容。甚至可容纳的数据类型也不同,因为TVarRec 支持Delphi 数据类型,而TVarData 支持OLE 数据类型。
    TVarRec 记录类型结构如下:
    type
    TVarRec = record
    case Byte of
    vtInteger:    (VInteger: Integer; VType: Byte);
    vtBoolean:    (VBoolean: Boolean);
    vtChar:       (VChar: Char);
    vtExtended:   (VExtended: PExtended);
    vtString:     (VString: PShortString);
    vtPointer:    (VPointer: Pointer);
    vtPChar:      (VPChar: PChar);
    vtObject:     (VObject: TObject);
    vtClass:      (VClass: TClass);
    vtWideChar:   (VWideChar: WideChar);
    vtPWideChar:  (VPWideChar: PWideChar);
    vtAnsiString: (VAnsiString: Pointer);
    vtCurrency:   (VCurrency: PCurrency);
    vtVariant:    (VVariant: PVariant);
    vtInterface:  (VInterface: Pointer);
    end;
    每种记录都有一个VType 域,乍一看不容易发现,因为它与实际意义的整型类型数据(通常是一个引用或一个指针)放在一起,只被声明了一次。
    利用上面信息我们就可以写一个能操作不同类型数据的函数。下例的SumAll 函数,通过把字符串转成整数、字符转成相应的序号、True布尔值加一,计算不同类型数据的和。这段代码以一个case语句为基础,虽然不得不经常通过指针取值,但相当简单,:
    function SumAll (const Args: array of const): Extended;
    var
    I: Integer;
    begin
    Result := 0;
    for I := Low(Args) to High (Args) do
    case Args [I].VType of
    vtInteger: Result :=
    Result   Args [I].VInteger;
    vtBoolean:
    if Args [I].VBoolean then
    Result := Result   1;
    vtChar:
    Result := Result   Ord (Args [I].VChar);
    vtExtended:
    Result := Result   Args [I].VExtended^;
    vtString, vtAnsiString:
    Result := Result   StrToIntDef ((Args [I].VString^), 0);
    vtWideChar:
    Result := Result   Ord (Args [I].VWideChar);
    vtCurrency:
    Result := Result   Args [I].VCurrency^;
    end; // case
    end;
    Delphi 调用协定 
    32位的Delphi 中增加了新的参数传递方法,称为fastcall:只要有可能,传递到CPU寄存器的参数能多达三个,使函数调用操作更快。这种快速调用协定(Delphi 3确省方式)可用register 关键字标示。
    问题是这种快速调用协定与Windows不兼容,Win32 API 函数必须声明使用stdcall 调用协定。这种协定是Win16 API使用的原始Pascal 调用协定和C语言使用的cdecl 调用协定的混合体。
    除非你要调用外部Windows函数或定义Windows 回调函数,否则你没有理由不用新增的快速调用协定。
    什么是方法? 
    如果你使用过Delphi 或读过Delphi 手册,大概已经听说过“方法”这个术语。方法是一种特殊的函数或过程,它与类这一数据类型相对应。在Delphi 中,每处理一个事件,都需要定义一个方法,该方法通常是个过程。不过一般“方法”是指与类相关的函数和过程。
    下面是Delphi 自动添加到窗体源代码中的一个空方法:
    procedure TForm1.Button1Click(Sender: TObject);
    begin
    {here goes your code}
    end;
    Forward 声明 
    当使用一个标识符(任何类型)时,编译器必须已经知道该标识符指的是什么。为此,你通常需要在例程使用之前提供一个完整的声明。然而在某些情况下可能做不到这一点,例如过程A调用过程B,而过程B又调用过程A,那么你写过程代码时,不得不调用编译器尚未看到其声明的例程。
    欲声明一个过程或函数,而且只给出它的名字和参数,不列出其实现代码,需要在句尾加forward 关键字:
    procedure Hello; forward;
    在后面应该补上该过程的完整代码,不过该过程代码的位置不影响对它的调用。下面的例子没什么实际意义,看过后你会对上述概念有所认识:
    procedure DoubleHello; forward;
    
    procedure Hello;
    begin
    if MessageDlg ('Do you want a double message?',
    mtConfirmation, [mbYes, mbNo], 0) = mrYes then
    DoubleHello
    else
    ShowMessage ('Hello');
    end;
    
    procedure DoubleHello;
    begin
    Hello;
    Hello;
    end;
    上述方法可用来写递归调用:即DoubleHello 调用Hello,而Hello也可能调用DoubleHello。当然,必须设置条件终止这个递归,避免栈的溢出。
    尽管 forward 过程声明在Delphi中不常见,但是有一个类似的情况却经常出现。当你在一个单元(关于单元的更多内容见下一章)的interface 部分声明一个过程或一个函数时,它被认为是一个forward声明,即使没有forward关键字也一样。实际上你不可能把整个例程的代码放在interface 部分,不过你必须在同一单元中提供所声明例程的实现。
    类内部的方法声明也同样是forward声明,当你给窗体或其组件添加事件时, Delphi会自动产生相应的代码。在TForm 类中声明的事件是forward 声明,事件代码放在单元的实现部分。下面摘录的源代码中有一个Button1Click 方法声明:
    type
    TForm1 = class(TForm)
    ListBox1: TListBox;
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
    end;

    

 

相关新闻

每一个程序员要遵守的一些优秀编程风格
代词it, that, one, the one 的辨析
《VB程序设计基础》选择题
Is Family Life Changing
设计模式之Iterator
设计模式之Visitor
设计模式之Interpreter(解释器)
设计模式之Mediator(中介者)
设计模式之Strategy(策略)
设计模式之State

您可能对这些感兴趣  

第7讲 综合分析BIM在运用与推广中的障碍问题
考核学生质量的困难
日本福岛核电站事故初步分析
论科技异化与科技人化
中国科技计划项目管理现状与对策
财政科技资金的审计重点和内容
软科学研究机构在政府决策中的功效、困境及对策研究
打造超级DOS系统(下)
DOS命令基础应用(6)
打造超级DOS系统(上)

题目筛选器
日期:
类型:
状态:
得分: <=
分类:
作者:
职业:
关键字:
搜索

 
 
 
  焦点事件
 
  知识体系
 
  职业列表
 
 
  最热文章
 
 
  最多引用文章
 
 
  最新文章
 
 
 
 
网站介绍 | 广告服务 | 招聘信息 | 保护隐私权 | 免责条款 | 法律顾问 | 意见反馈
版权所有 不得转载
沪ICP备 10203777 号 联系电话:021-54428255
  帮助提示    
《我的太学》是一种全新的应用,您在操作中遇到疑问或者问题,请拨打电话13564659895,15921448526。
《我的太学》