在Unit2 中定义类如下:
uses Dialogs;
type
 TMyClass=class
  public
    procedure ABC;
 end;
.....
procedure TMyClass.ABC;
begin
 Showmessage('OK');
end;然后在Unit1做如下操作:
uses Unit2;
procedure TForm1.Button1Click(Sender: TObject);
var a:TMyClass;
begin
  a.ABC;  //运行时会出现Showmessage('OK')的对话框
end;我想问的是:为什么TMyClass类不用实例化就能访问它的方法ABC?
按照我的理解应该是这样的调用的:
var a:TMyClass;
 a:=TMyClass.Create; //先实例化
 a.ABC;              //再调用方法 
 a.Free;             //最后释放
-------有什么错误或理解模糊的吗??  请指点。  

解决方案 »

  1.   

    来自:wr960204, 时间:2003-9-18 13:29:00, ID:2184994
    因为不管是实例的方法还是类的方法,都存放于类空间里,只有域变量(成员变量)才存放于实例中。
    所以只要你的方法里面没用到实例的成员变量,不用创建实例也可调用成功。因为他是到类的代码空间去执行的。  
    来自:白衣书生, 时间:2003-9-18 13:38:00, ID:2185017
    to wjsht:
     真的是我说的那样的。
     你可作个测试。to wr960204:
     多谢。
     >>实例的方法还是类的方法,都存放于类空间里。
     意思就是说“实例的方法还是类的方法”共用一段内存?还是分别使用的?
     这点我不明白。
     这上面写的这种情况中,是否子类都可以执行祖先类中的方法而不需要实例化?
     
    来自:jobsxy, 时间:2003-9-18 13:48:00, ID:2185044
    白衣书生,你只需这样测试一下就明白为什么了procedure TForm1.Button1Click(Sender: TObject);
    var a:TMyClass;
    begin
     showmessage(a.ClassParent.ClassName);
    end;

    a:=TMyClass.Create; //先实例化
      showmessage(a.ClassParent.ClassName);              //再调用方法 
    a.Free;     
    来自:白衣书生, 时间:2003-9-18 14:18:00, ID:2185133
    to jobsxy:
     更糊涂了。
     为什么没有实例化的对象的父类是 TButtonControl ?
     即showmessage(a.ClassParent.ClassName) 显示为 TButtonControl?
      
    来自:lzhg_kn, 时间:2003-9-18 14:20:00, ID:2185140
    纯属个人解,不一定正确。
    只要有对象的声明,类就会存在于内存。因此你可以调用静态方法,只要你调用的
    静态方法没有访问它的域,就不会出错。
     
    来自:chshanghai, 时间:2003-9-18 14:59:00, ID:2185266
    好问题, 我顶.
    有点同意 lzhg_kn  
    来自:foresail, 时间:2003-9-18 16:16:00, ID:2185528
    白衣书生:
       你的錯誤就在於,基礎不扎實.
       
    按照你的代碼,
    var a:TMyClass;
    就已經創建了對象.
    餘下的我不多說了.好好學習基本功吧.  
    来自:白衣书生, 时间:2003-9-18 16:23:00, ID:2185542
    to foresail:
    >按照你的代碼,
    >var a:TMyClass;
    >就已經創建了對象.
    var a:TMyClass; 这个不是类型的声明吗?此时难道就创建了对象?  
      

  2.   

    来自:jobsxy, 时间:2003-9-18 16:45:00, ID:2185615
    呵呵,这个问题很奇怪,如同你的测试结果一样,明明声明了a:TMyClass,可
    showmessage(a.classname)却是TButton,
    如果你在
    procedure TForm1.Label1Click(Sender: TObject);
    var a:TMyClass;
    begin
    showmessage(a.ClassName);
    end;
    你又会发现a居然是TLabel类。由此我有点同意wr960204的说法,所谓a.abc能正常执行可能是被误导了,并不是正确的作法,还是做个测试
    type
    TMyClass=class
     public
       vstr:String;
       procedure ABC;
    end;
    .....
    procedure TMyClass.ABC;
    begin
    Showmessage(self.vstr);
    end;然后在Unit1做如下操作:
    uses Unit2;
    procedure TForm1.Button1Click(Sender: TObject);
    var a:TMyClass;
    begin
     a.vstr := 'ok';
     a.ABC;
    end;
    好象也很正常,可退出时就出错了.....................,其中原由还是高手来给我们讲解讲解。  
    来自:白衣书生, 时间:2003-9-18 16:57:00, ID:2185651
    jobsxy:
     按照你写的代码作了一下,果然是这样。
     你的代码中是不是用到了“成员变量”? 这按照wr960204的说法,是不能使用的啊。  
    来自:fu_qi_ming, 时间:2003-9-18 17:43:00, ID:2185806
    好帖.听.  
    来自:lu_sam, 时间:2003-9-18 17:53:00, ID:2185830
    因为你没有写构造及构析啊,实际你调用的只是类的方法!  
    来自:wr960204, 时间:2003-9-18 18:45:00, ID:2185918
    type
     TMyClass=class
      private
        FMsg:String; 
      public
        procedure ABC;
     end;
    .....
    procedure TMyClass.ABC;
    begin
     Showmessage(FMsg);
    end;
    再按你上面的方式调用就不行了,因为FMsg是成员变量,属于实例的。
    所以还是我上面说的,只要不涉及到实例成员变量的方法就可以调用。因为方法是存放于类的空间里的  
    来自:foresail, 时间:2003-9-19 8:13:00, ID:2186335
    >to foresail:
    >>按照你的代碼,
    >>var a:TMyClass;
    >>就已經創建了對象.
    >var a:TMyClass; 这个不是类型的声明吗?此时难道就创建了对象?  var a:TMyClass  是類型聲明?
    那麼 type  是干什麼用的?
     
    来自:majorsoft, 时间:2003-9-19 13:40:00, ID:2187352
    下面说说我的看法吧
    1.var a:TMyClass; //并没有创建TmyClass对象!!!
    不信?你把a:TmyClass作为TForm1的一个成员看看。它是定义了一个指向TmyClass对象的指针/引用。当它作为TForm1的成员时,在对象构造时,初始化为nil,但定义为函数的局部变量时,它并没有初始化为nil,所以导致
    请看下面的代码:
    unit Unit1;interfaceuses
     Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
     Dialogs, StdCtrls,unit2;type
     Tmyclass=class
     public
       procedure showMsg;
     end;
     TForm1 = class(TForm)
       Button1: TButton;
       procedure FormCreate(Sender: TObject);
       procedure Button1Click(Sender: TObject);
     private
       { Private declarations }
      a:TMyClass
     public
       { Public declarations }
     end;var
     Form1: TForm1;
     form2:Tform2;
    implementation{$R *.dfm}{ Tmyclass }procedure Tmyclass.showMsg;
    begin
     showmessage('ok');
    end;procedure TForm1.Button1Click(Sender: TObject);
    var
     a:TMyClass;
    begin
     application.CreateForm(Tform2,form2);
     a.showMsg;
     //运行时会出现Showmessage('OK')的对话框,为什么?因为所对象的方法在其类中共享函数段,即使对象没有创建,同样也可以调用其方法。
     if assigned(a) then
     begin
      form2.memo1.Lines.Add('a:'+a.ClassName);//  为什么是显示是Tbutton?看下面的解释
       //因为是函数栈的对象,对象引用并没有初始化为nil
       form2.memo1.Lines.Add('a'+inttostr(integer(a))); //  1
     end;
     form2.memo1.Lines.Add('button1首地址'+inttostr(integer(button1)));//
      //1,2结果显示一样的
      //说明对象引用/指针在函数栈并没有初始化,此时它指向button1了,
      //其实classname函数相当与一个虚函数。
      //不信?请看看className的原形:
      {
       vmtClassName         = -44;    //Virtual method table entries
       vmtMethodTable       = -52;   //虚拟方法表入口地址//class function TObject.ClassName: ShortString;
    //{$IFDEF PUREPASCAL}
    //begin
     //Result := PShortString(PPointer(Integer(Self) + vmtClassName)^)^;
    //注意vmtClassName
    //end;
      //a.classname当然显示为 Tbutton
     form2.show;
    end;
      
    来自:majorsoft, 时间:2003-9-19 13:50:00, ID:2187381
    可以修改下程序把
    a:TmyClass作为Tform的一个成员,在窗口对象构造时,初始化为nil,
    那么a.showMsg();可以正常执行,为什么?因为ShowMsg是静态方法,编译器根据其(类名)来确定方法的地址,
    但下面的
    form2.memo1.Lines.Add('a:'+a.ClassName);会出错,因为a.ClassName方法中使用了
    Result := PShortString(PPointer(Integer(Self) + vmtClassName)^)^;
    vmtclassName'就是className方法的入口地址,相当于虚方法,它要根据(实际的对象)来确定要调用的方法。此时,对象并未创建,所以就会出错了。  
    来自:foresail, 时间:2003-9-19 16:53:00, ID:2187976
    To majorsoft:
       能讓你解釋出來還真不容易.
    我只指出幾點.
    1.--->//因为是函数栈的对象,对象引用并没有初始化为nil
    前半句正確,後半句似是而非.
    var a:TMyClass    //確確實實創建了對象,就在你說的"函数栈"里.,創建的同時系統自動調用類的默認constructor.
    2.var a:TMyClass 不存在什麼指針的問題,這就是一個對象實體.
    3.showmessage是誰的靜態方法? 再說showmessage跟我們討論的根本無關,因為我們程序里調用的是a.ABC,這裡ABC定義成TMyClass的靜態方法了嗎?怪不得現在有這麼多人哀嘆Delphi程序員工資低.Delphi程序員的水平不高工資怎麼能高?
    呵呵,說說而已,別扔板磚.  
    来自:foresail, 时间:2003-9-19 17:02:00, ID:2188003
    補充:這個類比較特殊,它沒有成員變量.所以它的默認構造函數不會出問題.
    如果有成員變量,必須自定義一個constructor  
    来自:majorsoft, 时间:2003-9-19 18:41:00, ID:2188222
    to foresail,
     看来你是个典型的C++主义者。C++学的不错哦。
     我确实犯了些错误,因为不存在栈对象,所有的对象都在heap堆中创建的,请原谅!
    在C++中:
     TmyClass a;
    这样确实创建了对象,因为它使用的是栈内存,它调用默认构造函数。
    但我要提醒你的是这是在delphi中。它们的实现机制还是有点不同,其实用堆来存储对象有其优点,他让用户自己显示地创建和销毁,什么时候创建什么销毁了,一清二楚,可读性非
    常好,而且我用函数创建对象,并不需要拷贝,等等优点。如果用栈的话,真有点不好,这都要感谢Object Pascal的发明者们
    对于你说的第三点,请看清楚我的程序:
    Tmyclass=class
    public
      procedure showMsg;
    end;
    procedure Tmyclass.showMsg;
    begin
    showmessage('ok');
    end;
    我是说的是"因为ShowMsg是静态方法"。
    最后再重申明一下,在delphi中所有的对象名都是引用/指针!这又与C++中不同!
    在对某样事物不了解的情况下,我们应该学会闭嘴。
    对于你的“
    怪不得現在有這麼多人哀嘆Delphi程序員工資低.Delphi程序員的水平不高工資怎麼能高?”
       你说的是事实,我现在水平也很菜,delphi的VCL帮我们做我很多事情,使得入门也很容易,使用很方便(不知道你为什么会用delphi,也是看中她这一点吧),但这往往促使我们delphi程序员的懒惰性。VCL比那个MFC不知好到那儿去了,我打算学习VCL,尤其是它的设计思想,以摆脱菜鸟帽子,有同想法者,欢迎加我QQ122646527,认证delphi or VCL如果上面各位,有什么问题!可以通知我删除此贴!
      
     
      

  3.   

    来自:wr960204, 时间:2003-9-18 13:29:00, ID:2184994
    因为不管是实例的方法还是类的方法,都存放于类空间里,只有域变量(成员变量)才存放于实例中。
    所以只要你的方法里面没用到实例的成员变量,不用创建实例也可调用成功。因为他是到类的代码空间去执行的。  支持!支持!
      

  4.   

    说明一个错误之处,“所以只要你的方法里面没用到实例的成员变量,不用创建实例也可调用成功”这句话是错误的。一个类,只有被调用RegisterClass()方法后,他的方法才会进入到可调配的类当中。响应的方法还有FindClass;
      

  5.   

    当然,
    Var a: TMyType;
    这样做系统会默认帮你调用一下 RegisterClass(TMyType);接下来
    a := TMyType.Create;
    系统实际上等于执行如下语句 a := FindClass(TMyType).Create;
      

  6.   

    对于语句
    Var a: TMyTypeDELPHI编译的时候会做两个动作,
    一个是为a开辟空间,
    另外一个就是在申明这个变量的局域的代码头部自动加上如下代码
    if not FindClass(TMyType) then RegisterClass(TMyType);
      

  7.   

    TO:防守反击 能解析一下下面的吗?
    showmessage(a.classname)却是TButton,
    如果你在
    procedure TForm1.Label1Click(Sender: TObject);
    var a:TMyClass;
    begin
    showmessage(a.ClassName);
    end;
    你又会发现a居然是TLabel类。由此我有点同意wr960204的说法,所谓a.abc能正常执行可能是被误导了,并不是正确的作法,还是做个测试
    type
    TMyClass=class
     public
       vstr:String;
       procedure ABC;
    end;
    .....
    procedure TMyClass.ABC;
    begin
    Showmessage(self.vstr);
    end;然后在Unit1做如下操作:
    uses Unit2;
    procedure TForm1.Button1Click(Sender: TObject);
    var a:TMyClass;
    begin
     a.vstr := 'ok';
     a.ABC;
    end;
    好象也很正常,可退出时就出错了.....................,其中原由还是高手来给我们讲解讲解。  
      

  8.   

    楼上接旨:
    改成如下代码,就更明白了
    procedure TForm1.Button1Click(Sender: TObject);
    var a:TMyClass;
    begin
     ShowMessage(TButton(a).Caption);
     a.vstr := 'ok';
     a.ABC;
     ShowMessate(Button1.Caption);
    end;或者
    procedure TForm1.Button1Click(Sender: TObject);
    var a:TMyClass;
    begin
     ShowMessage(TButton(a).Caption);
     a := Nil;
     a.vstr := 'ok';
     a.ABC;
    end;程序根本运行不了。因为在"a := nil" 之前,a.vstr是访问的Button1的内存!!!
    在组件的事件代码里定义的类变量,其初始值都被设置为 Sender了。
    纠正一个地方:
    对于语句
    Var a: TMyTypeDELPHI编译的时候会做两个动作,
    一个是为a开辟空间,(这里是说指针a自己的空间,不是说指向的内容空间)
    另外一个就是在申明这个变量的局域的代码头部自动加上如下代码
    if FindClass(TMyType) <> nil then RegisterClass(TMyType);
      

  9.   

    procedure TForm1.Button1Click(Sender: TObject);
    var a:TMyClass;
    begin
     ShowMessage(TButton(a).Caption);
     a.vstr := 'ok';
     a.ABC;
     ShowMessate(Button1.Caption);
    end;这里a.vstr访问的 基地址是:Button1,偏移地址是vstr(很可能就是0),也就是访问Button1的数据空间里的0偏移假如改成如下代码
    TMyClass = class
        temp: array[0.9999,0..9999] of Int64;
        vStr: String;
        procedure ABC;
    end;你再看看
    procedure TForm1.Button1Click(Sender: TObject);
    var a:TMyClass;
    begin
     a.vstr := 'ok';
    end;
    我敢肯定报错!因为 vstr的偏移值在Button1里溢出了!
      

  10.   

    纠正一下,是
    TMyClass = class
        temp: array[0..9999,0..9999] of Int64;
        vStr: String;
        procedure ABC;
    end;上面的代码我没有去测试过,但我敢肯定报错,再也不会象你们说的那样居然正常只是退出不正常了!而且你知道为什么你们的实验在退出的时候抱错吗?是因为 a.vstr := 'ok' 破坏了Button1,造成窗体关闭的时候不能正常释放Button1!
      

  11.   

    老K,不好意思,没有留言,这两天没有机器,
    unit Unit1;interfaceuses
      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;  TMyClass=class
     public
       vstr:String;
       procedure ABC;
    end;var
      Form1: TForm1;implementation{$R *.dfm}procedure TMyClass.ABC;
    begin
    Showmessage(self.vstr);
    end;procedure TForm1.Button1Click(Sender: TObject);
    var a:TMyClass;
    begin
    showmessage(a.ClassName);
    end;
    end.之所以显示TButton; 是由于编译器优化的结果,
    对于ShowMessage(a.ClassName);
    汇编玛:lea edx, [ebp-$00000104]
    mov eax,[ebx]    //这个ebx恰好就是这个Sender值,也就是Button1值,[ebx]就是VPTR
    call TObject.ClassName如果在
    procedure TForm1.Button1Click(Sender: TObject);之前加上{$o-}
    就会出错.wush007(防守反击):不同意你的那个说法'当然,
    Var a: TMyType;
    这样做系统会默认帮你调用一下 RegisterClass(TMyType);接下来
    a := TMyType.Create;
    系统实际上等于执行如下语句 a := FindClass(TMyType).Create;'Var a: TMyType;
    编译器并不会调用RegisterClass,这里只是声明一个TMyType类型的变量,当第一次声明该类型的变量时编译器会构建这个TMyType的类表,也就是一些属于该类的信息,虚表这些,另外
    Var a: TMyType;编译器只是保留了一个四字节的变量,用于指向一个TMyType的对象空间,并不存在RegisterClass(TMyType)的调用

    a := TMyType.Create;
    也不会调用FindClass(TMyType).Create;
    而是直接调用TMyType.Create,
    这时编译器会在TMyType的构照函数中插入_classCreate,NewInstance, InitInstance这些
    函数,用于分配内存,初始化对象的数据成员,就这些对了,老K,工作怎样了
      

  12.   

    而要详细的跟踪这个ebx的改变,我的跟踪如下:
    当点击一个TButton时,窗体收到WM_COMMAND消息,调用
    procedure TCustomForm.WMCommand(var Message: TWMCommand);
    begin
      with Message do
        if (Ctl <> 0) or (Menu = nil) or not Menu.DispatchCommand(ItemID) then
          inherited;
    end;调用
    procedure TWinControl.WMCommand(var Message: TWMCommand);
    begin
      if not DoControlMsg(Message.Ctl, Message) then inherited;
    end;在调用function DoControlMsg(ControlHandle: HWnd; var Message): Boolean;
    var
      Control: TWinControl;
    begin
      DoControlMsg := False;
      Control := FindControl(ControlHandle);
      if Control <> nil then
        with TMessage(Message) do
        begin
          Result := Control.Perform(Msg + CN_BASE, WParam, LParam);
          DoControlMsg := True;
        end;
    end;调用function TControl.Perform(Msg: Cardinal; WParam, LParam: Longint): Longint;
    var
      Message: TMessage;
    begin
      Message.Msg := Msg;
      Message.WParam := WParam;
      Message.LParam := LParam;
      Message.Result := 0;
      if Self <> nil then WindowProc(Message);
      Result := Message.Result;
    end;ebx的改变就是在这个Perform中的
      if Self <> nil then WindowProc(Message);
    这个时候改变的
    if Self <> nil then WindowProc(Message);对应的汇编为
    cmp dword ptr [ebp-$04],$00   //[ebp-$04]即为Self
    jz +$0c
    lea edx, [ebp-$20]
    mov ebx, [ebp-$04]   //将Self保存在ebx中
    mov eax, [ebx+$3c]         
    call dword ptr [ebx+$38] 而Perform会调用CNCommand,CNCommand会调用Click,接着就调用OnClick
      

  13.   

    楼上不要光说不做
    请问
    Type
    TMyClass = class;
    TMyClass1 = class(TMyClass);
    TMyClass2 = class(TMyClass);
    CMyClass = class of TMyClass;
    var
      MyClass: TMyClass;
      TheClass: CMyClass;procedure Test;
    begin
      TheClass := TMyClass1;
      MyClasss := TheClass.Create;
      TheClass := TMyClass2;
      MyClasss := TheClass.Create;
    end;上面这段代码 TheClass.Create是调用那个类的Create?
      

  14.   

    不知道你说光说不做,要我做什么,对于你这个例子,如下
    unit Unit1;interfaceuses
      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;  TMyClass = class
      public
        constructor Create;
      end;  TMyClass1 = class(TMyClass)
      public
        constructor Create;
      end;  TMyClass2 = class(TMyClass)
      public
        constructor Create;
      end;  CMyClass = class of TMyClass;var
      Form1: TForm1;implementation{$R *.dfm}var
      MyClass: TMyClass;
      TheClass: CMyClass;procedure Test;
    begin
      TheClass := TMyClass1;
      MyClass := TheClass.Create;
      TheClass := TMyClass2;
      MyClass := TheClass.Create;
    end;
    procedure TForm1.Button1Click(Sender: TObject);
    begin
      Test;
    end;{ TMyClass }constructor TMyClass.Create;
    begin
      ShowMessage('TMyClass.Create');
    end;{ TMyClass1 }constructor TMyClass1.Create;
    begin
     ShowMessage('TMyClass1.Create');
    end;{ TMyClass2 }constructor TMyClass2.Create;
    begin
      ShowMessage('TMyClass2.Create');
    end;end.结果都显示TMyClass.Create;,
    你想说明什么??
      

  15.   

    haha,
    换成 constructor TMyClass1.Create; override看看。
      

  16.   

    对了,告诉楼上,你的OB语言理解还很差,回去买点人参补一补。TMyClass construcotr Create; virtual;
    TMyClass1 construcotr Create; override;
    TMyClass2 construcotr Create; override;
      

  17.   

    unit Unit1;interfaceuses
      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;  TMyClass = class
      public
        constructor Create;virtual;
      end;  TMyClass1 = class(TMyClass)
      public
        constructor Create;override;
      end;  TMyClass2 = class(TMyClass)
      public
        constructor Create;override;
      end;  CMyClass = class of TMyClass;var
      Form1: TForm1;implementation{$R *.dfm}var
      MyClass: TMyClass;
      TheClass: CMyClass;procedure Test;
    begin
      TheClass := TMyClass1;
      MyClass := TheClass.Create;
      TheClass := TMyClass2;
      MyClass := TheClass.Create;
    end;
    procedure TForm1.Button1Click(Sender: TObject);
    begin
      Test;
    end;{ TMyClass }constructor TMyClass.Create;
    begin
      ShowMessage('TMyClass.Create');
    end;{ TMyClass1 }constructor TMyClass1.Create;
    begin
     ShowMessage('TMyClass1.Create');
    end;{ TMyClass2 }constructor TMyClass2.Create;
    begin
      ShowMessage('TMyClass2.Create');
    end;end.
      

  18.   

    这个我当然了解,还用你说
    你说的这个跟你上面的
    '
    当然,
    Var a: TMyType;
    这样做系统会默认帮你调用一下 RegisterClass(TMyType);接下来
    a := TMyType.Create;
    系统实际上等于执行如下语句 a := FindClass(TMyType).Create;
    '
    这个有什么关系.另外,小声的问一句, OB语言是什么,呵呵
      

  19.   

    没什么关系,我现在也讲不清楚。总之,我以前做一个“用户可以自己设计自身输入界面的软件(如添加一个继承于TEdit的TMyEdit,设置他的属性)”的时候,我虽然声明了TMyEdit的变量,但我创建的时候就提示我说“类没有注册”,最后我是调用RegisterClass解决的。哈哈,哈哈。