请大家看看这个问题,这段程序为什么能运行?

qiujoe 2003-08-25 10:46:52
在 TForm1.Button1Click 函数中,我声明了一个ttest类型的变量,未初始化,但运行成功。希望高手能帮我解释一下,我刚用delphi,谢谢了。(详见代码)


unit Unit1;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;

type
TTest = class;
TTest = class(TObject)
public
i: integer;
procedure test;
constructor Create;
end;

TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;

var
Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
var
test1: TTest;
begin
// 未初始化,为什么可以继续执行。
// test1 := TTest.Create;
test1.test;
test1.i :=5;
showmessage(inttostr(test1.i));
end;

{ TTest }

constructor TTest.Create;
begin
// abort;
end;

procedure TTest.test;
begin
showmessage('hello, world!');
end;

end.

我很奇怪,为什么不初始化也能运行???????

经过试验,我得出如下结论,请指教

如果不初始化,delphi也会分配一块内存,不过此内存是只读的。
1. 当test类中有成员变量字段时,如果对成员变量赋值,退出程序时才会报错。报内存只读错误。如果有成员变量,但没有赋值,也不会报错。

2. 如果当test类中没有成员变量字段,可以正常运行,且不报错。

3. 如果test类型的变量声明成临时变量与全局变量,其分配内存的方式是不一样的。全局变量,不为类中的字段变量分配内存。临时变量,为类中的字段变量分配内存,不过是只读的,但在赋值时不会出错,在退出程序时才报错。

各位高手验证一下我得出的结论,看看对不对。也请帮我详细解释一下delphi的内存分配机制。为什么不分配内存也可以执行方法?为什么不报错?
...全文
47 29 打赏 收藏 转发到动态 举报
写回复
用AI写文章
29 条回复
切换为时间正序
请发表友善的回复…
发表回复
cjfden 2003-09-17
  • 打赏
  • 举报
回复
迷糊
qiujoe 2003-08-28
  • 打赏
  • 举报
回复
结帐,请FrameSniper(§绕瀑游龙§) 去
http://expert.csdn.net/Expert/topic/2185/2185503.xml?temp=.5702783
接分,
cnhgj 2003-08-26
  • 打赏
  • 举报
回复
狂UP
IORILI 2003-08-26
  • 打赏
  • 举报
回复
虚心学习
qiujoe 2003-08-26
  • 打赏
  • 举报
回复
让大家在看看,希望对其它人也有所帮助
qiujoe 2003-08-26
  • 打赏
  • 举报
回复
3x
hiflower 2003-08-26
  • 打赏
  • 举报
回复
继续学习ing
bluenightsky 2003-08-26
  • 打赏
  • 举报
回复
FrameSniper 2003-08-26
  • 打赏
  • 举报
回复
To CaoWen2003
抄也要知道原理,况且这个代码《Mastering Delphi 4》上就有!而且比这个还多,你要不要啊?我给你介绍的几本书你能认真的理解就不错了,怕的就是你买了那些书而不去认真看,最后还不如不买,永远都是知其然不知其所以然!
nyf1220 2003-08-26
  • 打赏
  • 举报
回复
哈哈,他学我啊
不过能让大家知道真相也是好事啊,不管从哪里抄
caowen2003 2003-08-26
  • 打赏
  • 举报
回复
framesniper这小子一天到晚抄高手突破混分
鄙视啊
FrameSniper 2003-08-26
  • 打赏
  • 举报
回复
楼上说的没有一个正确的!!!!

更正一下:没有看清楚故国兄和NYF1220的回复,他们的回复是正确的!SORRY!

----------------------------------------------------------------------

“TTest.Create;只是用来初始化test1的成员,在此之前,test1的空间和TTest的虚拟方法表已经被编译器分配好了。对于方法成员来说,如果如果它没有存取字段成员,都是可以在使用Create之前正常调用的。”----这里的Test1的空间只是栈上四个字节的对象指针控件,不要理解成对象实体控件!另外,故国兄说的如果没有存取字段成员,我想解释一下,这里说的字段成员实际上一般来说就是在类申明中定义的(一般)以F开头的数据成员,这些成员在类的VMT中是不存在的,他们只存在与对象实体的空间中,因为一般的同类对象之间的状态是不同的,而这些数据成员就是记录状态,而对他们进行操作的读、写方法就是改变状态!

-------------------------------------------------------------------------

“要创建一个对象的时候,首先分配了对象所站的内存空间,然后才利用构造函数,初试化个数据成员,也就是说,在没有掉用create只前,这个对象的内存空间已经被创建,而数据成员却没有,所以如果你赋值肯定出错。”----对于NYF1220的话,我也想补充一下:当我们在程序代码中调用构造函数的时候,编译器会在构造函数所处行之前自动插入如下的代码:
test dl,dl
jz +$08
add esp,-$10
call @ClassCreate //在这里将调用System.pas单元中的函数_ClassCreate(申明在1348行,实现在9018行,参考D7),并且使用这个函数来为对象在栈上分配实际的内存空间。所以说对象到了这个时候才真正地存在于系统为你的程序创建的进程空间的栈上。因此,如果没有调用Create构造器,我们通过Var申明的对象(指针)只是一个内容为初始化内容的指针,其内容是没有实际意义的。所以这里调用访问私有数据的OP语句是报内存错误!

------------------------------------------------------------------------

希望对各位有帮助!同时留下一个问题:为什么在不调用Create构造器的情况下,编译器仍然可以通过一个简单的对象指针来进入到正确的类VMT中寻找方法入口地址,中间的过程是如何实现的?希望各位讨论...........
GreenWaterBlueSky 2003-08-26
  • 打赏
  • 举报
回复
呵呵,第四次看见类似的问题了
FrameSniper 2003-08-25
  • 打赏
  • 举报
回复
楼上说的没有一个正确的!!!!

首先我们应该熟悉Delphi的对象模型到底是如何的结构,在Delphi的对象模型中,存在三个区域,一个是栈中的对象指针占用的四个字节的栈空间,其二是栈中对象实体占用的对象空间,最后一个是堆中类实体VMT(实际上叫这个名字并不贴切,因为VMT中不单单存放的虚拟方法的信息)占用的堆空间。这三块空间可以形成一个链,第一个空间中存放的是第二个空间的首地址,而第二个空间的前四字节组成的指针变量空间中存放的是第三个空间内部相对偏移地址为零处的绝对地址值。这就是Delphi对象模型在内存结构上的布局。

对于第二个空间,需要注意的一点是,这里只是存放类实例(对象)的数据成员,至于方法信息一概不在这里存在,而是在堆中VMT中存放对应的方法名称地址对照表(姑且这么叫),通过对象空间的VPTR(即头四个字节的指针内容)可以直接告诉编译器去堆中的什么地方的某个表中进行查找,从而获取相关方法的入口地址,从而最终成功调用对应方法,至于参数则根据方法的条用规则在其他地方存储,例如寄存器!

对于Delphi编译器来说,当我们使用var关键字进行对象的申明的时候只是分配第一个空间,而此时第二个空间是根本不存在,至于这个空间,只有明确的调用了类的构造器后才会在栈上开辟第二个空间,同时进行初始化;而对于第三个空间,此时由于编译器的编译顺序,这个空间在堆中是已经存在的!所以,考虑你的代码:
procedure TForm1.Button1Click(Sender: TObject);
var
test1: TTest; //编译器对这个申明语句的解释就是开辟第一个空间,此时第二空间不存在,而第三空间在编译器碰到上面的类申明的时候就已经分配了!所以存在!
begin
//test1 := TTest.Create;----本应该编译器看到这个句子才开始开辟第二空间,但由于你注释掉了,所以程序中根本没有第二空间,所以test1只是一个野指针!
test1.test; //虽然没有第二空间,但编译器仍然可以进入第三空间查找方法入口地址,所以这里可以调用成功;但这里存在一个问题,由于我们前面说了三个空间组成一个链,而在断链的情况下编译器仍能顺利进入第三空间进行方法的查找,我唯一可以想到的就是只有通过RTTI信息进行这种定位了,有待讨论!
test1.i :=5; //第二空间不存在,所以内存错误
showmessage(inttostr(test1.i)); //同上道理
end;


另外AD,我们上次讨论的那个问题根本就没有讨论清楚,至于风焱的解答我认为和讨论的问题没有多大关系!
FrameSniper 2003-08-25
  • 打赏
  • 举报
回复
楼上说的没有一个正确的!!!!

首先我们应该熟悉Delphi的对象模型到底是如何的结构,在Delphi的对象模型中,存在三个区域,一个是栈中的对象指针占用的四个字节的栈空间,其二是栈中对象实体占用的对象空间,最后一个是堆中类实体VMT(实际上叫这个名字并不贴切,因为VMT中不单单存放的虚拟方法的信息)占用的堆空间。这三块空间可以形成一个链,第一个空间中存放的是第二个空间的首地址,而第二个空间的前四字节组成的指针变量空间中存放的是第三个空间内部相对偏移地址为零处的绝对地址值。这就是Delphi对象模型在内存结构上的布局。

对于第二个空间,需要注意的一点是,这里只是存放类实例(对象)的数据成员,至于方法信息一概不在这里存在,而是在堆中VMT中存放对应的方法名称地址对照表(姑且这么叫),通过对象空间的VPTR(即头四个字节的指针内容)可以直接告诉编译器去堆中的什么地方的某个表中进行查找,从而获取相关方法的入口地址,从而最终成功调用对应方法,至于参数则根据方法的条用规则在其他地方存储,例如寄存器!

对于Delphi编译器来说,当我们使用var关键字进行对象的申明的时候只是分配第一个空间,而此时第二个空间是根本不存在,至于这个空间,只有明确的调用了类的构造器后才会在栈上开辟第二个空间,同时进行初始化;而对于第三个空间,此时由于编译器的编译顺序,这个空间在堆中是已经存在的!所以,考虑你的代码:
procedure TForm1.Button1Click(Sender: TObject);
var
test1: TTest; //编译器对这个申明语句的解释就是开辟第一个空间,此时第二空间不存在,而第三空间在编译器碰到上面的类申明的时候就已经分配了!所以存在!
begin
//test1 := TTest.Create;----本应该编译器看到这个句子才开始开辟第二空间,但由于你注释掉了,所以程序中根本没有第二空间,所以test1只是一个野指针!
test1.test; //虽然没有第二空间,但编译器仍然可以进入第三空间查找方法入口地址,所以这里可以调用成功;但这里存在一个问题,由于我们前面说了三个空间组成一个链,而在断链的情况下编译器仍能顺利进入第三空间进行方法的查找,我唯一可以想到的就是只有通过RTTI信息进行这种定位了,有待讨论!
test1.i :=5; //第二空间不存在,所以内存错误
showmessage(inttostr(test1.i)); //同上道理
end;


另外AD,我们上次讨论的那个问题根本就没有讨论清楚,至于风焱的解答我认为和讨论的问题没有多大关系!
qiujoe 2003-08-25
  • 打赏
  • 举报
回复
谢了,先
koma2003 2003-08-25
  • 打赏
  • 举报
回复
UP
myling 2003-08-25
  • 打赏
  • 举报
回复
你这个问题其实在这贴我们讨论过

http://expert.csdn.net/Expert/topic/2144/2144169.xml?temp=.6631128


是不是很类似?看看风炎的回答
nyf1220 2003-08-25
  • 打赏
  • 举报
回复
哈哈,我也发现了,button1 怎么不见了,真是…………
qiujoe 2003-08-25
  • 打赏
  • 举报
回复
我记得如果在Create(AOwner),指定了AOwner,可以不用自己管理生命期,delphi会在父对象结束时自动释放子对象的空间。如果没有指定AOwner需要自己释放空间。

对于这种非正常(或错误)的用法,生命期应该怎么管理。还是说可以不管?
为什么我用test1.free 会把button给释放掉???
加载更多回复(9)

5,388

社区成员

发帖
与我相关
我的任务
社区描述
Delphi 开发及应用
社区管理员
  • VCL组件开发及应用社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧