delphi中物件的create和free

2021-06-06 01:13:41 字數 2925 閱讀 4239

在d中每個應用程式可以獲得的記憶體空間分為兩種:堆(heap)和棧(stack)。

堆又稱為「自由儲存區」,其中的記憶體空間的分配與釋放是必須由程式設計師來控制的。例如,用getmem函式獲取了一定大小的記憶體空間,則在使用完後,必須呼叫freemem函式將空間釋放,否則就會發生所謂的「記憶體洩漏」。「借債還錢,天經地義」。

棧又稱為「自動儲存區」,其中的記憶體空間的分配與釋放是由編譯器和系統自動完成的,不需要程式設計師過問。函式呼叫時按值傳遞的引數所佔空間、函式中的區域性變數等,都是在棧中被分配空間的。

objecgt pascal遵循所謂的「引用/值」模型。無論在引數傳遞還是變數定義中,簡單型別(如integer、cardinal、char以及record等) 被按值傳遞或使用,其記憶體空間從棧中分配。而複雜型別(class)則被按引用傳遞或使用,其記憶體空間從堆中分配。在object pascal中,所有物件(類型別的)都被建立在記憶體的堆空間上,而非棧上。因此在建立物件時,其建構函式不會被編譯器自動呼叫,也沒有c++中所謂的 「預設建構函式」(預設建構函式:在c++中宣告乙個變數或乙個類例項時候,自動呼叫相應的購造函式為該變數或例項分配記憶體控制項,在d中的類型別物件都需 要程式設計師呼叫建構函式對其進行顯式構造,分配記憶體)呼叫建構函式來建立物件以及呼叫析構函式來消滅物件都是程式設計師的職責。

定義建構函式使用constructor關鍵字。按慣例,建構函式名稱為create(當然也可以用其他名稱,但那絕非優良的設計)。如

public

constructor create(st***thername, strmothername : string);

要建立出乙個物件,首先需要分配物件本身所占用的記憶體空間,然後執行類的建構函式,以初始化各資料成員、申請物件需要的資源或建立其內部包含的子物件。構 造函式**呼叫的是system.pas檔案的第8949行的_classcreate函式(以delphi 6為準),該函式具體為每個物件分配合適的記憶體。這個動作也就是所謂的「編譯器魔法」(compiler magic),由這個動作完成真正的物件的記憶體分配,乙個物件在這個時候已經有了外殼。

記憶體分配完成後是呼叫類的建構函式,即tmyclass.create(),以初始化資料成員。建構函式由定義類的程式設計師編寫,也就是說,將物件初始化成何種模樣是由程式設計師決定的。至此,乙個物件已經誕生了。

由於物件本身所佔記憶體的分配是由編譯器完成的,因此即使沒有建構函式,物件也一樣可以被構造。建構函式的職責只是初始化物件的資料成員,沒有建構函式只意 味著不會對資料成員進行初始化而已,編譯器會對所有資料進行清零初始化。此外,由於object pascal中所有類(除了tobject類本身)都是從tobject類派生,因此編譯器會呼叫tobject.create()建構函式。不過,這個 函式只是乙個空函式(只負責為類分配記憶體空間,但不進行類中成員的初始化工作)。

注意:在方法或函式中宣告乙個類型別變數的時候。編譯器給這個變 量乙個4位元組的位址空間在棧上。它並不代表任何東西,只是乙個指標而已(所以類型別變數名稱實質上都是指標而已,普通的賦值操作,賦予的也只是指標位址而 已,並沒有給新變數另闢空間儲存)。在函式或方法中,定義的類型別變數需要被初始化後才能使用,也就是被create後才能被正確訪問。create後在 堆中分配相應的空間,而宣告的變數則指向該段記憶體。但是當函式結束後如果該類型別變數不需要被傳遞到外界使用,那麼應該及時的free掉,也就是進行析 構,否則將發生記憶體洩漏,即該記憶體空間一直都被該變數占用。如果要傳遞到外界,那麼也就是result:=類型別變數a(result是編譯器給函式的默 認的乙個變數,未賦值前也是乙個4位元組位址控制項而已,負責傳遞變數)。result:=類型別變數a後,result指向了類型別變數a,也就是說 result和a都指向同乙個物件,修改result和a都是一樣的結果。這種情況下,也就是類型別變數a需要被傳遞時候,在函式內不需要被free.但 是函式將a傳遞給主調函式中的類型別變數b後,也就是b:=a,b也指向a的記憶體空間,那麼在b的使用期結束後就需要對b進行free,也就對a進行 free了,就不容易出現記憶體洩漏。

定義析構函式使用destructor關鍵字。按慣例,析構函式名稱為destroy。與建構函式類似,如果在類中沒有特殊的資源需要被釋放,也可以不定 義析構函式,tobject同樣定義了乙個空的析構函式。在析構物件的時候,應該呼叫物件的free()方法而不是直接呼叫destroy()。這是因 為,在tobject的free()方法中會判斷物件本身是否為nil,如果不為nil則呼叫物件的destroy(),以增加安全性。永遠不要直接呼叫 物件的destroy(),而應該是free()。要銷毀乙個物件,其順序與建立物件正好相反。首先是釋放物件申請的資源以及銷毀內部的子物件,之後是回 收物件本身所佔的記憶體空間。

銷毀乙個全域性物件變數的時候,通常將物件設定為nil,這樣就不會留下乙個含非法指標的變數。當析構函式或者乙個由析構函式呼叫的方法引用了該變數的 時候,如果該變數成了nil,則可以避免任何潛在的問題。free方法也不會自動將物件置為nil,所以在呼叫free之後,最好是在手動將物件設定為 nil。可以使用sysutils單元中的方法freeandnil過程——freeandnil(obj)。

建構函式和析構函式的目的在於乙個類可以像普通型別那樣初始化和銷毀,從而保證了封裝。在乙個類的建構函式中分配的資源盡量要記得在析構函式中銷毀。

另外,在vcl類中,物件的銷毀還應注意順序,vcl的組織形式:屬主(owner)機制。從tcomponent開始,vcl類的建構函式就帶有乙個參 數aowoner,它決定了這些類之間的樹型關係。比如乙個button放在form上,那麼button的aowoner就是form。那麼析構 form的時候,會先通知button析構。即析構的時候會通知自身擁有的物件先行析構,如此遞迴下去。這種屬主機制是設計模式中典型的結構性模式 composite(組合)。而遞迴析構的方式是典型的行為模式中的observer(觀察者模式),這是vcl中的訊息處理的基本模式。

總 結:簡單型別存放在棧中,由編譯器管理其記憶體空間。宣告時候就已經分配了記憶體空間。賦值後的兩個變數屬於不同的變數。但是類型別存放在堆中,需要程式設計師管 理,需要對其進行create和destroy,賦值以後被賦值的物件仍然指向賦值的變數記憶體空間,並沒有建立屬於自己的記憶體空間。

delphi中xmldocument物件使用方法

delphi 中的 xmldocument 類詳解 21 暫停 delphi 中的 xmldocument 類詳解 20 動態建立 xmldocument 物件 delphi 中的 xmldocument 類詳解 19 nodevalue 與 nodename 的讀寫區別 delphi 中的 xml...

Delphi物件指標小結

1 乙個物件指標指向的乙個物件的資料區,該資料區前面4個位元組是乙個指標,指向的是大名鼎鼎的vmt,其實是self的位址,如下 procedure tform1.btn3click sender tobject var chld tchild p1,p2,p3,p4 pointer begin ch...

Delphi物件指標小結

1 乙個物件指標指向的乙個物件的資料區,該資料區前面4個位元組是乙個指標,指向的是大名鼎鼎的vmt,其實是self的位址,如下 delphi view plain copy print?procedure tform1.btn3click sender tobject varchld tchild ...