诚心求教对象引用与克隆问题

wslmsx 2009-09-24 04:52:55
我正在看刘艺写的《Delphi面向对象编程思想》。看到此处有个不明白的地方。
3.1.5 对象的克隆:
书中举了个例子
(问题点1)
var a,b:TMyObject;
begin
a:=TMyObject.create;
b:=TMyObject.create;
b:=a;//错误,对象b丢失导致内存泄露。
end;
然后说明不应用b:=a;的形式,应写成b.assign(a);
以上说的我明白,即a和b各引用了不同的空间,而b:=a;这样的话b的那块空间就泄露了。所以用b.assign(a);这种是把值赋过来,而a和b还是有各自的空间。然后请往下看:
书中举了个例子,恢复字体的,是错误的方法
这个例子的想法是:初始memo中的字体是黑色的,然后点设置按钮设置后(比如设置成了红色),memo字体变为红色。再点恢复按钮后memo字体又变成了黑色。
但是问题点2是错误的,点恢复后恢复不成。
(问题点2)
FOriginalFont:TFont;
procedure TForm1.btnSetClick(Sender:TObject);//设置按钮
begin
if fontdialog1.Execute then
Memo1.Font:=FontDialog1.Font;
end;
procedure TForm1.FormCreate(Sender:TObject);
begin
Memo1.Lines.Add('某某某');
FOriginalFont:=Memo1.Font;
end;
procedure TForm1.btnundoClick(Sender:TObject);//恢复按钮
begin
Memo1.Font:=FOriginalFont;//Memo1.Font和FOriginalFont实际上引用的是同一个对象
end;
end.
然后举了个例子,是正确的方法
(问题点3)
FOriginalFont:TFont;
procedure TForm1.btnSetClick(Sender:TObject);
begin
if fontdialog1.Execute then
Memo1.Font:=FontDialog1.Font;
end;
procedure TForm1.FormCreate(Sender:TObject);
begin
Memo1.Lines.Add('某某某');
FOriginalFont:=TFont.create;
FOriginalFont.assign(Memo1.Font);
end;
procedure TForm1.btnundoClick(Sender:TObject);
begin
Memo1.Font.Assign(FOriginalFont);
end;
end.

问题点2和问题点3我也明白为什么对和错。2和3的区别主要是create事件中FOriginalFont的处理方法。然后我进行了机上实验,把问题点3改了一下:
(问题点4)
FOriginalFont:TFont;
procedure TForm1.btnSetClick(Sender:TObject);
begin
if fontdialog1.Execute then
Memo1.Font:=FontDialog1.Font;
end;
procedure TForm1.FormCreate(Sender:TObject);
begin
Memo1.Lines.Add('某某某');
FOriginalFont:=TFont.create;
FOriginalFont.assign(Memo1.Font);
end;
procedure TForm1.btnundoClick(Sender:TObject);
begin
Memo1.Font:=FOriginalFont;
end;
end.
问题点3和4的区别就最后1句话,一种是用assign(),一种是用:=。而问题点4的写法也能得到正确的结果,即可以恢复字体。

根据以上的例子,我得出结论并有问题。请帮我看下我的结论对不对,如果不对,请帮我更正我的看法。并帮我解答我的问题,谢谢!
结论:
因为问题点4可以得到正确的结果,所以b:=a;只看这个语句,这种写法,是b引用a的空间。a的空间变了,b才变。而之后无论b的空间变与不变,不影响a.(如问题点2.上来就FOriginalFont:=Memo1.Font;而之后Memo1.Font变了,所以FOriginalFont也变了。所以最后结果不正确。而如问题点4.最后写成Memo1.Font:=FOriginalFont的形式,如果设置并恢复很多次memo1,FOriginalFont也不会变,还是初始的黑色字体,所以问题点4能正确运行)
问题:
关于问题点1的内存泄露问题。问题点2,3,4不存在这种问题么?问题点3是书中完全正确的例子,就拿问题点3来说。memo1.font有一块空间。而Memo1.Font:=FontDialog1.Font;这句,Memo1.Font的空间不就指成了FontDialog1.Font的空间了么,Memo1.Font的空间不也泄露了么。要我说就换成Memo1.Font.assign(FontDialog1.Font);所以我觉得问题点2,3,4中font部分只要用了:=符号,都有这个问题.(因为FOriginalFont,Memo1.Font,fontdialog1这3个应该是都有各自的空间的,只要用了:=,就应该出问题点1的内存泄露问题)。而2,3,4为什么没有出1中的内存泄露问题呢?

我是新手,对引用与克隆这块不是很明白,有可能我的结论和问题中的话本身就有概念问题,请帮忙更正过来。请帮我解释下我的问题,如果我有概念性错误,也请帮我解答。我主要是想把这块搞明白,谢谢!
...全文
125 10 打赏 收藏 转发到动态 举报
写回复
用AI写文章
10 条回复
切换为时间正序
请发表友善的回复…
发表回复
火龙岛主 2009-09-25
  • 打赏
  • 举报
回复
1.Memo1.Font:=FontDialog1.Font;这句,Memo1.Font的空间不就指成了FontDialog1.Font的空间了么?
事实上不是这样的,首先我们看一下memo1的继承关系 tmemo继承于TCustomMemo,而TCustomMemo继承于TCustomEdit,TCustomEdit继承于TWinControl,TWinControl继承于TControl,TControl中实现了对Font的赋值,我们看一下他的实现方法:
property Font: TFont read FFont write SetFont stored IsFontStored;
从这里我们可以看出,他的赋值方法是通过SetFont方法实现的,那么他是如何实现的呢?
我们接着看代码:
procedure TControl.SetFont(Value: TFont);
begin
FFont.Assign(Value);
end;

现在清楚了吧,实际上Memo1.Font:=FontDialog1.Font最终的实现是如下语句表达的
memo1.font.assign(FontDialog1.Font),因此也是克隆,不存在地址引用问题!

FOriginalFont:=Memo1.Font 与Memo1.Font:=FontDialog1.Font是不同的。
具体还要看具体类的实现方法!

wxsan 2009-09-25
  • 打赏
  • 举报
回复
关注!
wxieyang 2009-09-24
  • 打赏
  • 举报
回复
另外,不要过分迷信Delphi的编译器,虽然这个编译器很好。
学一门语言,就要下功夫去了解这门语言的基础和实质,只有这样,才能写出高质量的代码。编译环境仅仅是为了让你方便地使用一个语言的工具而已。
wxieyang 2009-09-24
  • 打赏
  • 举报
回复
没报错不代表你的程序没有错误,也不代表你的程序没有内存泄漏。
Delphi的编译不出错误仅仅是表示你的语法没问题,不出经过仅仅表示你没有写一些可能存在隐患没用的语句。
但并不表示你的所有的语句都是正确的。
var
a: PChar;
begin
a := GetMemory(100);
a^ := 'a';
a := GetMemory(100);
a^ := 'a';
a := GetMemory(100);
a^ := 'a';
a := GetMemory(100);
a^ := 'a';
a := GetMemory(100);
a^ := 'a';
这样的语句一样不会报编译错误或者警告,但是却有大量的内存被泄漏了。
Memo1.Lines.Add('某某某');
FOriginalFont:=TFont.create;//多了这句话,明确使FOriginalFont有自己的空间。其它不变
FOriginalFont:=Memo1.Font
这表示你刚刚开辟个内存,然后又把唯一指向这块内存的引用(也就是指针),赋值成别的值了,当然就会泄漏内存了。
其实,还是你对delphi中的使用var声明的类变量没有搞清楚。
var
fn: TFont
注意,fn本身是4字节大小的一个变量,上面的语句实际上是声明了一个指向TFont的一个指针fn
fn := TFont.Create;
就是开辟了一个TFont大小的内存,然后把这个内存的对应的地址给fn;仅此而已。


FOriginalFont:=Memo1.Font
会导致 FOriginalFont 和 Memo1中的 FFont 成员变量指向同一个地址空间。

Memo1.Font:=FOriginalFont;
由于TMemo的 Font 存在属性方法,其实现部分仅仅是Assign一下,
这样的结果就是Memo1的FFont成员变量和FOriginalFont指向的不是同一个地址空间,仅仅是进行了一次值复制。
wslmsx 2009-09-24
  • 打赏
  • 举报
回复
回5楼,问题点5我在编译和运行时都没有报错啊,这是什么情况?是说这种问题即使出错了也不报出来么?
阿发伯 2009-09-24
  • 打赏
  • 举报
回复
问题点5比问题点2更糟糕,非但不能恢复字体,还存在内存泄露,必须把FOriginalFont:=Memo1.Font改为FOriginalFont.Assign(Memo1.Font)
wslmsx 2009-09-24
  • 打赏
  • 举报
回复
我是楼主,谢谢楼上2位的回复。
2楼讲的,我理解了,再次感谢!但还是有一处疑问。如你说的,Memo1.Font:=FontDialog1.Font;和Memo1.Font:=FOriginalFont这两种情况下,是设置Memo1的font属性,所以不涉及到Memo1.Font的引用变化即内存泄露的问题。而如果Memo1.Font在:=的右边,如问题点2中FOriginalFont:=Memo1.Font;这句,书的作者也说了FOriginalFont和memo1.font用了同样的引用,可见左边的FOriginalFont是对象,它的引用确实发生了变化。我现在把问题点2改一下,变成问题点5:
就是把问题点2的
Memo1.Lines.Add('某某某');
FOriginalFont:=Memo1.Font;
变成
//问题点5
Memo1.Lines.Add('某某某');
FOriginalFont:=TFont.create;//多了这句话,明确使FOriginalFont有自己的空间。其它不变
FOriginalFont:=Memo1.Font
然后运行问题点5这个程序,程序只是没有达到恢复字体的功能,但是编译运行时不报错和警告。这个和问题点1不是同类问题么?为什么说问题点1内存泄露了而问题点5运行时没有报错呢?
ZuoBaoquan 2009-09-24
  • 打赏
  • 举报
回复
勤思考很好,语言基础也很重要,楼主再加油!
ZuoBaoquan 2009-09-24
  • 打赏
  • 举报
回复
呵呵,你被迷惑了,Memo1.Font是属性,设置属性的值实际上调用的是对应的setter。e.g.修改Font属性实际上调用的是SetFont过程,下面是实际的源代码:

procedure TControl.SetFont(Value: TFont);
begin
FFont.Assign(Value);
end;

这里是不能修改对象引用的,只能赋值。
kampan 2009-09-24
  • 打赏
  • 举报
回复
1是已经b:=TMyObject.create,分配了内存空间 不同于234中的Memo1.Font ;
个人观点 仅供参考

16,748

社区成员

发帖
与我相关
我的任务
社区描述
Delphi 语言基础/算法/系统设计
社区管理员
  • 语言基础/算法/系统设计社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

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