在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; //最后释放
-------有什么错误或理解模糊的吗?? 请指点。
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; //最后释放
-------有什么错误或理解模糊的吗?? 请指点。
因为不管是实例的方法还是类的方法,都存放于类空间里,只有域变量(成员变量)才存放于实例中。
所以只要你的方法里面没用到实例的成员变量,不用创建实例也可调用成功。因为他是到类的代码空间去执行的。
来自:白衣书生, 时间: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; 这个不是类型的声明吗?此时难道就创建了对象?
呵呵,这个问题很奇怪,如同你的测试结果一样,明明声明了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如果上面各位,有什么问题!可以通知我删除此贴!
因为不管是实例的方法还是类的方法,都存放于类空间里,只有域变量(成员变量)才存放于实例中。
所以只要你的方法里面没用到实例的成员变量,不用创建实例也可调用成功。因为他是到类的代码空间去执行的。 支持!支持!
Var a: TMyType;
这样做系统会默认帮你调用一下 RegisterClass(TMyType);接下来
a := TMyType.Create;
系统实际上等于执行如下语句 a := FindClass(TMyType).Create;
Var a: TMyTypeDELPHI编译的时候会做两个动作,
一个是为a开辟空间,
另外一个就是在申明这个变量的局域的代码头部自动加上如下代码
if not FindClass(TMyType) then RegisterClass(TMyType);
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;
好象也很正常,可退出时就出错了.....................,其中原由还是高手来给我们讲解讲解。
改成如下代码,就更明白了
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);
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里溢出了!
TMyClass = class
temp: array[0..9999,0..9999] of Int64;
vStr: String;
procedure ABC;
end;上面的代码我没有去测试过,但我敢肯定报错,再也不会象你们说的那样居然正常只是退出不正常了!而且你知道为什么你们的实验在退出的时候抱错吗?是因为 a.vstr := 'ok' 破坏了Button1,造成窗体关闭的时候不能正常释放Button1!
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,工作怎样了
当点击一个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
请问
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?
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;,
你想说明什么??
换成 constructor TMyClass1.Create; override看看。
TMyClass1 construcotr Create; override;
TMyClass2 construcotr Create; override;
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.
你说的这个跟你上面的
'
当然,
Var a: TMyType;
这样做系统会默认帮你调用一下 RegisterClass(TMyType);接下来
a := TMyType.Create;
系统实际上等于执行如下语句 a := FindClass(TMyType).Create;
'
这个有什么关系.另外,小声的问一句, OB语言是什么,呵呵