• procedure of object 对象的函数指针


    应用:http://www.cnblogs.com/del88/p/6361117.html

    有 class of object ----- 类的类型

    那么自然有 方法的类型,方法的类型 分为两种:

    普通方法:

    function abc(): string;

    类的方法

    function TLei.abc(): string;

    所以定义方法的类型的时候 就有如下区别:

      TPro1 = procedure;//一个普通的无参数过程指针
    
      TPro2 = procedure(a: string); //一个普通的有参数过程指针
    
      TLeiPro1 = procedure of object; //类的 无参数 过程指针
    
      TLeiPro2 = procedure(a: Integer); //类的 有参数 过程指针
    
      TFun1 = function: string; //普通的 无参数 方法指针
    
      TFun2 = function(a: Integer): string;// 普通的 有参数 方法指针
    
      TLeiFun1 = function: string of object; //类的 无参数 方法指针
    
      TLeiFun2 = function(a: Integer): string of object; //类的 有参数 方法指针

    根据英文意思你就知道了,procedure of object(一个对象的过程)

    看测试代码:

    procedure TForm1.BBB(I: Integer);
    begin
      showmessage('BBB:' + IntToStr(I));
    end;
    
    procedure TForm1.CCC(I: Integer);
    begin
      showmessage('CCC:' + IntToStr(I));
    end;
    
    procedure TForm1.Button1Click(Sender: TObject);
      type
        Taaa = procedure (i:integer) of object ;
    var
      ap: Taaa;
    begin
      ap := BBB;
      ap(1);
      ap := CCC;
      ap(2);
    end;

     ===============下面是摘抄网上 更详尽的理解==================

    理论:     //适用于实现不是某一特定过程或函数
              type 
                   TNotifyEvent = procedure(Sender: TObject) of object; 
          首先:procedure 也是类型,可以理解为过程类型,定义过程的参数结构,而具体的实现可以动态赋值 
     onclick那样例子: 
         声明:  onclick= procedure(Sender: TObject,a :integer) of object;以后你就可以把TNotifyEvent作为过程用了,而不用考虑它实现什么功能,你想onclik  里用户写了什么,都是一样的 
            if assign(onclick) then 
              onclick; 
         这样只要TNotifyEvent可以执行,程序就去执行它,控件就是这样实现事件的 
         procedure 指针占4个字节,保存在过程的地址. procedure of object占8个字节,保存过程的地址和类的地址 
         procedure of object 是类过程(类函数),用法如下: 
             1、与普通函数相同点:可以像一般的函数和过程一样在类的实例里作为对象方法调用; 
             2、与普通函数不同点:可以不通过对象,而是通过类来直接调用。 
        也就是说,一般函数和过程必须通过类的实例(对象)来调用,而类过程(类函数)则可以不必通过该类的实例来调用。通过类来调用函数或者过程,可以定义和实现一些不能或者不适合作为某一特定对象行为的方法。 
      应用:
         delphi中经常见到以下两种定义 
         Type 
             TMouseProc = procedure (X,Y:integer);  //一种普通的过程
             TMouseEvent = procedure (X,Y:integer) of Object;//一种对象方法的类型 
    
          两者样子差不多但实际意义却不一样,两者的区别就在于TMouseEvent类型的方法必须在一个对象里。类方法存在一个隐藏参数self,也就是说两者形参不一样,所以不能相互转换 .
       TMouseProc只是单一的函数指针类型; 
       TMouseEvent是对象的函数指针,也就是对象/类的函数/方法 
     procedure   TForm1.BBB(I:   Integer); 
    begin 
        showmessage( 'BBB: '   +   IntToStr(I)); 
    end; 
    
    procedure   TForm1.CCC(I:   Integer); 
    begin 
        showmessage( 'CCC: '   +   IntToStr(I)); 
    end; 
    
    procedure   TForm1.Button1Click(Sender:   TObject); 
        type 
            Taaa   =   procedure   (i:integer)   of   object   ; 
    var 
        ap:   Taaa; 
    begin 
        ap   :=   BBB; //这里有一个隐含Self,完整格式: self.BBB;这里把BBB这个方法赋给ap这个变量,注意BBB与ap的声明原型要一样,不然会有错
        ap(1); 
        ap   :=   CCC; 
        ap(2); 
    end;
    理论:     //适用于实现不是某一特定过程或函数
    
              type 
                   TNotifyEvent = procedure(Sender: TObject) of object; 
          首先:procedure 也是类型,可以理解为过程类型,定义过程的参数结构,而具体的实现可以动态赋值 
     onclick那样例子: 
         声明:  onclick= procedure(Sender: TObject,a :integer) of object;以后你就可以把TNotifyEvent作为过程用了,而不用考虑它实现什么功能,你想onclik  里用户写了什么,都是一样的 
            if assign(onclick) then 
              onclick; 
         这样只要TNotifyEvent可以执行,程序就去执行它,控件就是这样实现事件的 
         procedure 指针占4个字节,保存在过程的地址. procedure of object占8个字节,保存过程的地址和类的地址(我认为这句说的好) 
         procedure of object 是类过程(类函数),用法如下: 
             1、与普通函数相同点:可以像一般的函数和过程一样在类的实例里作为对象方法调用; 
             2、与普通函数不同点:可以不通过对象,而是通过类来直接调用。 
        也就是说,一般函数和过程必须通过类的实例(对象)来调用,而类过程(类函数)则可以不必通过该类的实例来调用。通过类来调用函数或者过程,可以定义和实现一些不能或者不适合作为某一特定对象行为的方法。 
      应用:
         delphi中经常见到以下两种定义 
         Type 
             TMouseProc = procedure (X,Y:integer);  //一种普通的过程
             TMouseEvent = procedure (X,Y:integer) of Object;//一种对象方法的类型 
    ------------------------------------------------------------
    TMyEvent = procedure of object;
    实际上,TMyEvent类型是包含两个域的一个记录,其中一个域是Code,也就是和TMyProc一样的,另一个域是Data,他保存着一个对象。
    
      Button2.OnClick := Button1Click;
    这样的赋值,实际上是将窗体类的对象form1给data域,将Button1Click这个函数的地址给Code域
    
    --------------------------
    
    TMouseProc = procedure (X,Y:integer);
    
    TMouseEvent = procedure (X,Y:integer) of Object;
    
    TMouseProc只是单一的函数指针类型;
    
    TMouseEvent是对象的函数指针,也就是对象/类的函数/方法
    
    区别在于类方法存在一个隐藏参数self,也就是说两者形参不一样,所以不能相互转换。
    
    这也就是为什么delphi中可以这样赋值 button1.onClick:=button2.onClick;
    
    却不能这样赋值 button1.onclick=buttonclick; (buttonclick为本地函数,button2.onclick为类方法)的原因!
    其实要了解这些东西,适当的学些反汇编,WINDOWS内存管理机制,PE结构,看下李维的VCL架构剖析可以很好理解
    type
      TMyEvent = procedure of object;
    这是一种数据类型的定义,他定义了一个可以在类中使用的函数类型
    区别于
    type
      TMyProc = procedure;
    
    TMyEvent 和 TMyProc 都定义了一个函数类型,他们的差别是,TMyProc 不可以用在类中定义事件,TMyEvent 却可以。
    
    如果你想知道问什么,那就需要深入了解事件类型以及函数类型到底是个什么?
    
    procedure a();
    begin
      ShowMessage()
    end;
    
    var
      Func1: TMyProc;
      Func2: TMyEvent;
    
      func1 := A;
    
    func1 := A;
    
    这句到底发生了什么事情呢?
    
    在32位的windows操作系统中,任何一个进程被执行,操作系统要完成的工作基本上包括:为这个进程开辟一个线性的虚拟地址表(从$00000000到$FFFFFFFF),然后把exe程序加载到这个地址表对应的内存中,然后跳入到exe程序的入口点,剩下的事情,就是exe自己从入口点开始一步一步干自己的事情了。
    程序中,所有的函数都被编译到固定的位置,也就是每个函数都有一个地址与之对应,对函数的调用,实际上就是在寄存器或者堆栈中准备好参数,然后跳入到函数对应的地址继续执行。
    func1 := A;
    这个动作,完成的就是把函数A的地址赋值给func1这个变量。
    
    
    那么,为Func2: TMyEvent 赋值又是完成了什么呢?
    
    类中的函数也是有地址的,编译后,类中的函数也会被编译到一个固定的地址上。
    但是,func2不是表示一个函数地址,他是一个记录类型的变量
    这个可以通过 SizeOf(TMyEvent) 看到 SizeOf(TMyEvent) 返回值是8,而SizeOf(TMyProc)返回的是4
    实际上,TMyEvent类型是包含两个域的一个记录,其中一个域是Code,也就是和TMyProc一样的,另一个域是Data,他保存着一个对象。
    
      Button2.OnClick := Button1Click;
    这样的赋值,实际上是将窗体类的对象form1给data域,将Button1Click这个函数的地址给Code域
     
     
     
     
     
     
     
    1.
    TMyEvent = procedure of object; TMyEvent 是方法类型 就等于 String 是一种数据类型
    
    如在一个Txxx类中定义了一个过程  
    procedure Txxx.test;
    begin
      showmessage('1');
    end;
    
    那么就可以把 Txxx.test 赋给 TMyEvent 了 (TMyEvent := Txxx.test);
    因为 TMyEvent 的定义中有 of object 所以赋值给它的只能是类中的过程,而不能是普通过程。
    
    2.
    FOnHundred: TMyEvent; --> FOnHundred 是一个变量 它的类型是 TMyEvent,  
      就等于 icount : integer 这么简单
    
    3.
    property OnHundred: TMyEvent read FOnHundred write FOnHundred
    
    OnHundred 是一个属性 它的类型也是 TMyEvent 通过 FOnHundred 变量来存取 这个属性的值。
    
    当属性不用过程去存取的时候 (如上例)调用 OnHundred 和 FOnHundred 是相同的
     
    
    delphi中经常见到以下两种定义
    
    Type
    
    TMouseProc = procedure (X,Y:integer);
    
    TMouseEvent = procedure (X,Y:integer) of Object;
    
    两者样子差不多但实际意义却不一样,
    
    TMouseProc只是单一的函数指针类型;
    
    TMouseEvent是对象的函数指针,也就是对象/类的函数/方法
    
    区别在于类方法存在一个隐藏参数self,也就是说两者形参不一样,所以不能相互转换。
    
    这也就是为什么delphi中可以这样赋值 button1.onClick:=button2.onClick;
    
    却不能这样赋值 button1.onclick=buttonclick; (buttonclick为本地函数,button2.onclick为类方法)的原因!
    
    
    方法类型定义:TMethod = procedure of object;
    
     
    
    Procedural types allow you to treat procedures and functions as values that can be assigned to variables or passed to other procedures and functions. For example, suppose you define a function called Calc that takes two integer parameters and returns an integer:
    
    function Calc(X,Y: Integer): Integer;
    
    You can assign the Calc function to the variable F:
    
     
    
    var F: function(X,Y: Integer): Integer;
    
    F := Calc;
    
    If you take any procedure or function heading and remove the identifier after the word procedure or function, what’s left is the name of a procedural type. You can use such type names directly in variable declarations (as in the example above) or to declare new types:
    
    Type
    
    TIntegerFunction = function: Integer;
    
    TProcedure = procedure;
    
    TStrProc = procedure(const S: string);
    
    TMathFunc = function(X: Double): Double;
    
    Var
    
    F: TIntegerFunction;{ F is a parameterless function that returns an integer }
    
    Proc: TProcedure;   { Proc is a parameterless procedure }
    
    SP: TStrProc;       { SP is a procedure that takes a string parameter }
    
    M: TMathFunc;       { M is a function that takes a Double (real) parameterand returns a Double }
    
    procedure FuncProc(P: TIntegerFunction);  { FuncProc is a procedure whose only parameter is a parameterless integer-valued function }
    
    The variables above are all procedure pointers—that is, pointers to the address of a procedure or function. If you want to reference a method of an instance object (see Classes and objects), you need to add the words of object to the procedural type name. For example
    
    Type
    
    TMethod = procedure of object;
    
    TNotifyEvent = procedure(Sender: TObject) of object;
    
    These types represent method pointers. A method pointer is really a pair of pointers; the first stores the address of a method, and the second stores a reference to the object the method belongs to. Given the declarations
    
    Type
    
    TNotifyEvent = procedure(Sender: TObject) of object;
    
    TMainForm = class(TForm)
    
    procedure ButtonClick(Sender: TObject);
    
    ...
    
    end;
    
    var
    
    MainForm: TMainForm;
    
    OnClick: TNotifyEvent
    
    we could make the following assignment.OnClick := MainForm.ButtonClick;
    
    Two procedural types are compatible if they have the same calling convention,the same return value (or no return value), and the same number of parameters, with identically typed parameters in corresponding positions. (Parameter names do not matter.)
    
    Procedure pointer types are always incompatible with method pointer types. The value nil can be assigned to any procedural type.
    
    Nested procedures and functions (routines declared within other routines) cannot be used as procedural values, nor can predefined procedures and functions. If you want to use a predefined routine like Length as a procedural value, write a wrapper for it:
    
    function FLength(S: string): Integer;
    
    begin
    
    Result := Length(S);
    
    end;
  • 相关阅读:
    软件测试基础知识 + 面试理论(超详细)
    Qt 信号槽连接不成功问题原因汇总
    白平衡进化史
    Qt QPlainTextEdit多行文本编辑器
    C/C++变量命名规则
    win10 计算机管理员权限粘贴文件,高手分析win10往c盘粘贴文件需要权限的详细解决对策...
    常见的二十种软件测试方法详解(史上最全)
    C/C++ 回车符“0x0D”、“\n”,换行符“0x0A”、“\r
    C/C++ x86 x64下调用约定浅析
    C++ 命名规范
  • 原文地址:https://www.cnblogs.com/del88/p/6361000.html
Copyright © 2020-2023  润新知