|
1 Delphi中的對(duì)象模型: 2
1.1 對(duì)象名表示什么? 2
1.2 對(duì)象存儲(chǔ)在哪里? 2
1.3 對(duì)象中存儲(chǔ)了什么?它們是如何存儲(chǔ)的? 3
2 構(gòu)造函數(shù)與創(chuàng)建對(duì)象 5
2.1 什么是構(gòu)造函數(shù)?(“特殊的”類(lèi)方法) 5
2.2 對(duì)象的創(chuàng)建的全過(guò)程 5
2.3構(gòu)造函數(shù)另類(lèi)用法(使用類(lèi)引用實(shí)現(xiàn)構(gòu)造函數(shù)的多態(tài)性) 6
3 析構(gòu)函數(shù)與銷(xiāo)毀對(duì)象 7
3.1 什么是析構(gòu)函數(shù)(“天生的”虛方法) 7
3.2 對(duì)象銷(xiāo)毀的全過(guò)程 7
3.3 destroy, free, freeAndNil, release用法和區(qū)別 7
4 VCL構(gòu)造&析構(gòu)體系結(jié)構(gòu) 8
5 正確使用構(gòu)造函數(shù)和析構(gòu)函數(shù) 9
剖析Delphi中的構(gòu)造和析構(gòu)
摘 要: 本文通過(guò)對(duì)VCL/RTL的研究,來(lái)剖析構(gòu)造函數(shù)和析構(gòu)函數(shù)的實(shí)現(xiàn)機(jī)制和VCL中對(duì)象的體系結(jié)構(gòu),并說(shuō)明如何正確地創(chuàng)建和釋放對(duì)象。
關(guān)鍵字: 構(gòu)造,析構(gòu),創(chuàng)建對(duì)象,銷(xiāo)毀對(duì)象,堆,棧,多態(tài)。
作 者: majorsoft
問(wèn)題
Delphi中構(gòu)造函數(shù)和析構(gòu)函數(shù)的實(shí)現(xiàn)機(jī)制是什么?和C 有何不同?如何做到正確地創(chuàng)建和釋放對(duì)象?
解決思路
如何正確使用構(gòu)造和析構(gòu)是我們?cè)谑褂肈elphi過(guò)程中經(jīng)常遇到的問(wèn)題,在大富翁論壇中的Oriented Pascal欄目有不少相關(guān)帖子(詳見(jiàn)相關(guān)問(wèn)題),本人也曾遇到過(guò)類(lèi)似的問(wèn)題,下面通過(guò)對(duì)VCL/RTL源代碼的研究,來(lái)理解構(gòu)造函數(shù)和析構(gòu)函數(shù)的實(shí)現(xiàn)機(jī)制。
1 Delphi中的對(duì)象模型:
1.1 對(duì)象名表示什么?
與C 不同,Delphi中的對(duì)象名(也可以稱做變量)表示對(duì)象的引用,并不表示對(duì)象本身,相當(dāng)于指向?qū)ο蟮闹羔?這就所謂的“對(duì)象引用模型”。如圖所示:
Obj(對(duì)象名) 實(shí)際的對(duì)象
Vmt 入口地址
數(shù)據(jù)成員
圖1對(duì)象名引用內(nèi)存中的對(duì)象
1.2 對(duì)象存儲(chǔ)在哪里?
每個(gè)應(yīng)用程序?qū)⒎峙浣o其運(yùn)行的內(nèi)存分為四個(gè)區(qū)域:
代碼區(qū)(Code area)
全局?jǐn)?shù)據(jù)區(qū)(data area)
堆區(qū)(heap area)
棧區(qū)(stack area)
圖2 程序內(nèi)存空間
代碼區(qū):存儲(chǔ)程序中程序代碼,包括所有的函數(shù)代碼
全局?jǐn)?shù)據(jù)區(qū):存儲(chǔ)全局?jǐn)?shù)據(jù)。
堆區(qū):又叫“自由存儲(chǔ)區(qū)”,存儲(chǔ)動(dòng)態(tài)數(shù)據(jù)(在Delphi中包括對(duì)象和字符串)。作用域?yàn)檎麄€(gè)應(yīng)用程序的整個(gè)生命周期直到調(diào)用了析構(gòu)方法。
棧區(qū):又叫“自動(dòng)存儲(chǔ)區(qū)”存儲(chǔ)程序中的局部數(shù)據(jù),在C 中,局部變量實(shí)際上是auto類(lèi)型的變量。作用域?yàn)楹瘮?shù)內(nèi)部,函數(shù)調(diào)用完系統(tǒng)就立即回收棧空間。
在C 中,對(duì)象既可創(chuàng)建在堆(heap)上,也可以創(chuàng)建在棧(stack)中,還可以在全局?jǐn)?shù)據(jù)中創(chuàng)建對(duì)象,故C 有全局對(duì)象、局部對(duì)象、靜態(tài)對(duì)象和堆對(duì)象四種對(duì)象之說(shuō)。而在Delphi中,所有的對(duì)象都是建立堆(heap)存儲(chǔ)區(qū)上,所以Delphi構(gòu)造函數(shù)不能自動(dòng)被調(diào)用,而必須由程序員自己調(diào)用(在設(shè)計(jì)器拖動(dòng)組件,此時(shí)對(duì)象由Delphi創(chuàng)建)。下面的程序說(shuō)明Delphi和C 中創(chuàng)建對(duì)象的區(qū)別:
在Delphi中:
Procedure CreateObject(var FooObjRef:TFooObject);
begin
FooObjRef:=TfooObject.create;
//由程序員調(diào)用,過(guò)程調(diào)用完之后,對(duì)象依然存在.不需要進(jìn)行拷貝
FooObject.caption=’I am created in stack of CreateObject()’;
End;
而在C 中:
TfooObject CreateObject(void);
{
TfooObject FooObject;//創(chuàng)建局部對(duì)象
// static TfooObject FooObject;//創(chuàng)建靜態(tài)局部對(duì)象
//對(duì)象自動(dòng)調(diào)用默認(rèn)的構(gòu)造函數(shù)進(jìn)行創(chuàng)建,對(duì)象此時(shí)在函數(shù)棧中創(chuàng)建
FooObject.caption=’I am created in stack of CreateObject()’;
return FooObject;
//返回的時(shí)候進(jìn)行了對(duì)象拷貝,原來(lái)創(chuàng)建的對(duì)象隨函數(shù)的調(diào)用結(jié)束后,自動(dòng)銷(xiāo)毀}
TfooObject fooObject2;//創(chuàng)建全局對(duì)象。
void main();
{ TFooObject* PfooObjec=new TfooObject;
//創(chuàng)建堆對(duì)象。函數(shù)調(diào)用完之后,對(duì)象依然存在,不需要進(jìn)行拷貝。}
1.3 對(duì)象中存儲(chǔ)了什么?它們是如何存儲(chǔ)的?
與C 不同的是,Delphi中的對(duì)象只存儲(chǔ)了數(shù)據(jù)成員和虛擬方法表(vmt)的入口地址,而沒(méi)有存儲(chǔ)方法,如圖所示:
對(duì) 象 虛擬方法表 代碼段
Vmt地址
name:String
width:integer;
ch1:char;
…
Proc1
Func1
…
procn
funcn
…
圖 3 對(duì)象的結(jié)構(gòu) …
也許你對(duì)上面的說(shuō)法存在著些疑問(wèn),請(qǐng)看下面的程序:
TsizeAlignTest=class
private
i:integer;
ch1,ch2:char;
j:integer;
public
procedure showMsg;
procedure virtMtd; virtual;
end;
memo1.Lines.Add(inttostr(sizeTest.InstanceSize) '''':InstanceSize'''');
memo1.Lines.Add(inttostr(integer(sizeTest)) ''''<-start Addr'''');
memo1.Lines.Add(inttostr(integer(@(sizeTest.i))) ''''<-sizeTest.i'''');
memo1.Lines.Add(inttostr(integer(@(sizeTest.ch1))) ''''<-sizeTest.ch1'''');
memo1.Lines.Add(inttostr(integer(@(sizeTest.ch2))) ''''<-sizeTest.ch2'''');
memo1.Lines.Add(inttostr(integer(@(sizeTest.j))) ''''<-sizeTest.j'''');
結(jié)果顯示:
16:InstanceSize
14630724<-start Addr
14630728<-sizeTest.i
14630732<-sizeTest.ch1
14630733<-sizeTest.ch2
14630736<-sizeTest.j
數(shù)據(jù)成員和vmt入口地址就占了16個(gè)字節(jié)!,兩個(gè)成員函數(shù)showMsg, virtMtd在對(duì)象的存儲(chǔ)區(qū)中根本沒(méi)占空間。
那么成員函數(shù)到底存儲(chǔ)在哪兒呢?由于Delphi是基于RTL(運(yùn)行時(shí)類(lèi)型庫(kù))的,所有的成員函數(shù)都在類(lèi)中存儲(chǔ),成員函數(shù)實(shí)際上就是方法指針,它指向成員函數(shù)的入口地址,該類(lèi)的所有對(duì)象共享這些成員函數(shù)。那么怎樣找到成員函數(shù)的入口地址呢?對(duì)于靜態(tài)函數(shù),這個(gè)工作由編譯器來(lái)完成的,在編譯過(guò)程中,根據(jù)類(lèi)對(duì)象引用/指針的類(lèi)型,即直接在類(lèi)來(lái)中找到成員函數(shù)的入口地址(此時(shí)并不需要對(duì)象存在),這也就是所謂的靜態(tài)綁定;而對(duì)于虛方法(包括動(dòng)態(tài)方法),則是通過(guò)在運(yùn)行時(shí)的對(duì)象的虛擬方法表vmt入口地址(即對(duì)象的前四個(gè)字節(jié),此時(shí)對(duì)象一定要存在,否則就會(huì)導(dǎo)致指針訪問(wèn)出錯(cuò)),來(lái)找到成員函數(shù)的入口地址,這也就是所謂的動(dòng)態(tài)綁定。
注 意
上面提到,所有的成員函數(shù)都在類(lèi)中存儲(chǔ),實(shí)際上也包括虛擬方法表Vmt。從Delphi的代碼自動(dòng)完成功能(它依賴于編譯信息)可以看出,當(dāng)我們?cè)谳斎胪陮?duì)象名,再輸入“.“之后,此時(shí)Delphi重新編譯了一遍,列出所有的數(shù)據(jù)成員和所有的靜態(tài)方法,所有的虛方法,所有的類(lèi)方法,所有的構(gòu)造函數(shù)和析構(gòu)函數(shù),大家可以動(dòng)手試試看是不是這樣的。
類(lèi)虛方法表vmt入口地址
數(shù)據(jù)成員模板信息
靜態(tài)方法表等
虛方法表vmt
對(duì) 象
Vmt入口地址
數(shù)據(jù)成員
上面的程序還演示了對(duì)象數(shù)據(jù)成員的對(duì)齊方式(物理數(shù)據(jù)結(jié)構(gòu)),以4字節(jié)對(duì)齊(windows默認(rèn)的對(duì)齊方式),如下圖所示:
Vmt Entrance Addr
i
Ch1 Ch2
j
2 構(gòu)造函數(shù)與創(chuàng)建對(duì)象
2.1 什么是構(gòu)造函數(shù)?(“特殊的”類(lèi)方法)
從OO(面向?qū)ο螅┧枷氲恼Z(yǔ)義上講,構(gòu)造函數(shù)負(fù)責(zé)對(duì)象的創(chuàng)建,但就OOP語(yǔ)言的實(shí)現(xiàn)上講,無(wú)論Delphi還是C ,構(gòu)造函數(shù)充其量只做了對(duì)象的初始化工作(包含調(diào)用內(nèi)部子對(duì)象的構(gòu)造函數(shù)),并沒(méi)有負(fù)責(zé)創(chuàng)建對(duì)象的全過(guò)程(參考2.2)。
另外,與C 中不同的是,Delphi為構(gòu)造函數(shù)定義了另一種方法類(lèi)型(mkConstructor,參見(jiàn)Delphi安裝目錄下的\Source\RTL\Common\typInfo.pas,125行),我們可以把它理解為 “特殊的”類(lèi)方法。它只能通過(guò)類(lèi)(類(lèi)名/類(lèi)引用/類(lèi)指針)來(lái)調(diào)用,而一般的類(lèi)方法既可以通過(guò)類(lèi)也可以通過(guò)對(duì)象來(lái)調(diào)用;還有一點(diǎn)特殊就是構(gòu)造函數(shù)中內(nèi)置的self參數(shù)是指向?qū)ο蟮?,而在?lèi)方法中self是指向類(lèi)的,我們通常在其中對(duì)其數(shù)據(jù)成員進(jìn)行初始化工作,使其成為真正意義上的對(duì)象,這都得益于self這個(gè)參數(shù)。
在默認(rèn)情況下,構(gòu)造函數(shù)是靜態(tài)函數(shù),我們可以把它設(shè)為虛方法,在其派生類(lèi)中對(duì)其覆載(Override),這樣可以實(shí)現(xiàn)構(gòu)造函數(shù)的多態(tài)性(參見(jiàn)2.4),也可以對(duì)其進(jìn)行重載(Overload),創(chuàng)建多個(gè)構(gòu)造函數(shù),還可以在派生類(lèi)直接覆蓋(Overlay)父類(lèi)的構(gòu)造函數(shù),這樣在派生類(lèi)屏蔽了父類(lèi)的構(gòu)造函數(shù),在VCL中就采用了這些技術(shù),形成一個(gè)構(gòu)造&析構(gòu)的“體系結(jié)構(gòu)”(參見(jiàn)4)
2.2 對(duì)象的創(chuàng)建的全過(guò)程
對(duì)象的創(chuàng)建完整過(guò)程應(yīng)該包括分配空間、構(gòu)造物理數(shù)據(jù)結(jié)構(gòu)、初始化、內(nèi)部子對(duì)象的創(chuàng)建。上面提到,構(gòu)造函數(shù)只是負(fù)責(zé)初始化工作以及調(diào)用內(nèi)部子對(duì)象的構(gòu)造函數(shù),那么分配空間和構(gòu)造物理結(jié)構(gòu)是怎么完成的呢?這由于編譯器在做了額外的事情,我們不知道而已。編譯到構(gòu)造函數(shù)時(shí),會(huì)構(gòu)造函數(shù)之前,會(huì)在插入一行“call @ClassCreate”匯編代碼,它實(shí)際上就是system 單元中的_ClassCreate函數(shù),下面看看_ClassCreate函數(shù)的部分源碼:
function _ClassCreate(AClass: TClass; Alloc: Boolean): TObject;
asm
{ -> EAX = pointer to VMT }
{ <- EAX = pointer to instance }
…
CALL dword ptr [EAX].vmtNewInstance //調(diào)用NewInstance
…
End; {\Source\RTL\sys\system.pas,第8939行}
VmtNewInstance=-12; 它是NewInstance 函數(shù)在類(lèi)中的偏移量,則“CALL dword ptr [EAX].vmtNewInstance”實(shí)際上就是調(diào)用NewInstance,請(qǐng)看TObject.NewInstance:源碼:
class function NewInstance: TObject; virtual;
class function TObject.NewInstance: TObject;
begin
Result := InitInstance(_GetMem(InstanceSize));
end;
“InitInstance(_GetMem(InstanceSize))”依次調(diào)用了三個(gè)函數(shù):
1) 首先調(diào)用InstanceSize(),返回實(shí)際類(lèi)的對(duì)象大小
class function TObject.InstanceSize: Longint; //相當(dāng)于一個(gè)虛方法
begin
Result := PInteger(Integer(Self) vmtInstanceSize)^;//返回實(shí)際類(lèi)的對(duì)象大小
end;
2) 調(diào)用_GetMem()在堆上分配Instance大小的內(nèi)存,并返回對(duì)象的引用
3) 調(diào)用InitInstance()進(jìn)行構(gòu)造物理數(shù)據(jù)結(jié)構(gòu),并把成員設(shè)置默認(rèn)值,比如把整型的數(shù)據(jù)成員的值設(shè)為0,指針設(shè)為nil等。如果有虛方法,把虛擬方法表Vmt的入口地址賦給對(duì)象的前四個(gè)字節(jié)。
在調(diào)用完NewInstance之后,這個(gè)時(shí)候的對(duì)象,只有“空殼”,而沒(méi)有實(shí)際的“內(nèi)容”,所以就需要要調(diào)用定制的構(gòu)造函數(shù)對(duì)對(duì)象進(jìn)行有意義的初始化,以及調(diào)用內(nèi)部子對(duì)象的構(gòu)造函數(shù),使程序中的對(duì)象能真實(shí)反映現(xiàn)實(shí)世界的對(duì)象。這就是對(duì)象創(chuàng)建的全過(guò)程。
2.3構(gòu)造函數(shù)另類(lèi)用法(使用類(lèi)引用實(shí)現(xiàn)構(gòu)造函數(shù)的多態(tài)性)
在Delphi中,類(lèi)也是作為對(duì)象存儲(chǔ)的,所以同樣存在著多態(tài)性,它是借助類(lèi)引用和虛類(lèi)方法來(lái)實(shí)現(xiàn)的,這樣提供了類(lèi)一級(jí)的多態(tài)的實(shí)現(xiàn)。把類(lèi)方法設(shè)為虛方法,在其派生類(lèi)中覆載(override)它,再通過(guò)基類(lèi)的引用/指針調(diào)用它,這樣根據(jù)類(lèi)引用/指針指向?qū)嶋H類(lèi)來(lái)構(gòu)造對(duì)象。請(qǐng)看下面的程序:
TmyClass=class
constructor create;virtual;
end;
Ttmyclass=class of TmyClass;//基類(lèi)的類(lèi)引用
TmyClassSub=class(TmyClass)
constructor create; override;
end;
procedure CreateObj(Aclass:TTMyClass;var Ref);
begin
Tobject(Ref):=Aclass.create;
//ref為無(wú)類(lèi)型,和任何類(lèi)型都不兼容,所以使用時(shí)必須顯式強(qiáng)制轉(zhuǎn)換(cast)
//Aclass為類(lèi)引用,統(tǒng)一的函數(shù)接口,不同的實(shí)現(xiàn)。
//它會(huì)根據(jù)Aclass引用/指向的實(shí)際類(lèi)來(lái)構(gòu)造對(duì)象。
End;
…
CreateObj(TmyClass,Obj);
CreateObj(TmyClassSub,subObj);
3 析構(gòu)函數(shù)與銷(xiāo)毀對(duì)象
3.1 什么是析構(gòu)函數(shù)(“天生的”虛方法)
從OOP思想的語(yǔ)義上講,析構(gòu)函數(shù)負(fù)責(zé)銷(xiāo)毀對(duì)象,釋放資源。在Delphi中,同義。
Delphi為析構(gòu)函數(shù)也定義了一種方法類(lèi)型(mkConstructor,參見(jiàn)Delphi安裝目錄下的\Source\RTL\Common\typInfo.pas,125行),在VCL中,它實(shí)際是一種“天生的”虛方法,在VCL類(lèi)所有的祖先-Tobject中定義了“destructor Destroy; virtual; ”。為什么VCL要這么做呢?因?yàn)樗WC在多態(tài)情況下對(duì)象能正確地被析構(gòu)。如果不使用虛方法,則可能只析構(gòu)了基類(lèi)子對(duì)象,從而造成所謂的“內(nèi)存泄露”。所以為了保證正確地析構(gòu)對(duì)象,析構(gòu)函數(shù)都需要加override聲明。
3.2 對(duì)象銷(xiāo)毀的全過(guò)程
先銷(xiāo)毀派生類(lèi)子對(duì)象,再銷(xiāo)毀基類(lèi)子對(duì)象。
提 示
在派生類(lèi)中,基類(lèi)子對(duì)象指從基類(lèi)中繼承的部分,派生類(lèi)中子對(duì)象是指新增的部分。
3.3 destroy, free, freeAndNil, release用法和區(qū)別
destroy:虛方法
釋放內(nèi)存,在Tobject中聲明為virtual,通常是在其子類(lèi)中override 它,且要加上inherited關(guān)鍵字,才能保證派生類(lèi)對(duì)象正確地被銷(xiāo)毀;
但destroy一般不能直接用,為什么?
假如當(dāng)一個(gè)對(duì)象為nil,我們?nèi)匀徽{(diào)用destroy,此時(shí)會(huì)產(chǎn)生錯(cuò)誤。因?yàn)閐estroy是虛方法,它要根據(jù)對(duì)象中的頭四個(gè)字節(jié)找到虛擬方法表Vmt的入口地址,從而找到destroy的入口地址,所以此時(shí)對(duì)象一定要存在。但free就是靜態(tài)方法,它只需根據(jù)對(duì)象引用/指針的類(lèi)型來(lái)確定,即使對(duì)象本身不存在也沒(méi)問(wèn)題,而且在free中有判斷對(duì)象是否存在的操作, 所以用free比用destroy安全。
2)free:靜態(tài)方法
測(cè)試對(duì)象是否為nil, 非nil則調(diào)用destroy。下面是free的Delphi代碼:
procedure Tobject.Free;
begin
if Self <> nil then
Destroy;
end;
一靜一動(dòng),取長(zhǎng)補(bǔ)短,豈不妙哉!
不過(guò),調(diào)用Destroy只是把對(duì)象銷(xiāo)毀了,但并沒(méi)有把對(duì)象的引用設(shè)為nil,這需要程序員來(lái)完成,不過(guò)自從Delphi5之后,在sysUtils單元中提供了一個(gè)freeAndNil。
3)freeAndNil;一般方法,非對(duì)象方法,非類(lèi)方法。
SysUtils單元中FreeAndNil 定義
procedure FreeAndNil(var Obj);
var
Temp: TObject;
begin
Temp := TObject(Obj);
Pointer(Obj) := nil;
Temp.Free;
end;
建議大家用它代替free/Destroy,以便確保正確地釋放對(duì)象。
4)release;TcustomForm中定義的靜態(tài)方法。
當(dāng)窗口中所有的事件處理完之后,才調(diào)用free函數(shù)。常用在銷(xiāo)毀窗口,而在這個(gè)窗口中事件處理需要一定的時(shí)間的時(shí)候,用這個(gè)方法能確保窗口事件處理完之后才銷(xiāo)毀窗口。下面是TCustomForm.Release的Delphi源代碼:
procedure TCustomForm.Release;
begin
PostMessage(Handle, CM_RELEASE, 0, 0);
//向窗口發(fā)CM_RELEASE消息到消息隊(duì)列,當(dāng)所有的窗口事件消息處理完之后,
//再調(diào)用CM_RELEASE消息處理過(guò)程CMRelease
end;
再看看下面CM_RELEASE消息處理過(guò)程CMRelease的定義:
procedure CMRelease(var Message: TMessage); message CM_RELEASE;
procedure TCustomForm.CMRelease;
begin
Free; //最后還是free;
end;
4 VCL構(gòu)造&析構(gòu)體系結(jié)構(gòu)
TObject
constructor Create;//靜態(tài)方法
destructor Destroy; virtual;
TPersistent
destructor Destroy; override;
TComponent
constructor Create(AOwner: TComponent); virtual;
destructor Destroy; override;
TControl
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
…
下面分析VCL中的構(gòu)造和析構(gòu)的源代碼,以Tcontrol為例:
constructor TControl.Create(AOwner: TComponent);
begin
inherited Create(AOwner);//創(chuàng)建基類(lèi)子對(duì)象,并把析構(gòu)權(quán)移交給AOwner。放在最前面
//這樣就保證了“先創(chuàng)建基類(lèi)子對(duì)象,再創(chuàng)建派生類(lèi)子對(duì)象”的順序
…//初始化,以及調(diào)用內(nèi)部子對(duì)象的構(gòu)造函數(shù)
end;
destructor TControl.Destroy;
begin
…//析構(gòu)派生類(lèi)中內(nèi)部子對(duì)象
inherited Destroy;//析構(gòu)基類(lèi)對(duì)象,放在最后面
//這樣就保證了“先析構(gòu)派生類(lèi)子對(duì)象,再析構(gòu)基類(lèi)子對(duì)象”的順序
end;
5 正確使用構(gòu)造函數(shù)和析構(gòu)函數(shù)
經(jīng)過(guò)上面的分析,下面總結(jié)一下使用構(gòu)造函數(shù)和析構(gòu)函數(shù)的原則:
在使用對(duì)象之前,必須先建立一個(gè)對(duì)象時(shí),并且及時(shí)銷(xiāo)毀對(duì)象,以釋放資源。
兩個(gè)對(duì)象引用賦值時(shí),要確保出現(xiàn)的無(wú)名對(duì)象(指沒(méi)有被引用的對(duì)象)能被釋放。
當(dāng)創(chuàng)建一個(gè)組件時(shí),建議設(shè)置一個(gè)宿主組件(即使用AOwner參數(shù),通常是窗體),由Aowner來(lái)管理對(duì)象的銷(xiāo)毀,那么就不必惦記著銷(xiāo)毀該組件了,這是Delphi在窗體上/數(shù)據(jù)模塊設(shè)計(jì)并創(chuàng)建組件是采用的方法。所以我們不必書(shū)寫(xiě)調(diào)用該組件的析構(gòu)函數(shù)。
當(dāng)函數(shù)的返回類(lèi)型為對(duì)象時(shí),那么Result也是對(duì)象的引用,確保Result引用的對(duì)象要存在。
若要使用obj<>nil 或assigned(nil)測(cè)試對(duì)象存在時(shí),在調(diào)用析構(gòu)之后還應(yīng)obj:=nil。
請(qǐng)參考演示程序的源代碼
說(shuō)明(建議要有)
所有的Delphi程序已在win2k Delphi6 sp2 上通過(guò),對(duì)于C 程序,只是為了說(shuō)明與Delphi中不同,并不保證能直接運(yùn)行。為了加深對(duì)本篇文章的理解,建議參考演示程序。
這篇文章包括了我在學(xué)習(xí)VCL/RTL中的一些經(jīng)驗(yàn)和體會(huì),加上本人的個(gè)人能力有限,難免出現(xiàn)錯(cuò)誤,請(qǐng)大家不吝指正!
在閱讀本篇文章之前,需要讀者對(duì)Oriented Pascal語(yǔ)言有一定的了解,并能理解多態(tài),如果您對(duì)其中一些概念還不是很清楚的話,請(qǐng)參考相關(guān)文章。
通過(guò)本篇文章,你應(yīng)該能比較清楚地理解Delphi中的對(duì)象模型、構(gòu)造&析構(gòu)實(shí)現(xiàn)機(jī)制以及VCL中構(gòu)造&析構(gòu) 體系結(jié)構(gòu),并能掌握使用構(gòu)造&析構(gòu)的使用方法。Delphi中的構(gòu)造&析構(gòu)相當(dāng)于C 中的算是簡(jiǎn)單多了,我們應(yīng)該能掌握它。
|