构造函数 Create 与 析构函数 Destroy
參考了萬(wàn)一的博客:
http://www.cnblogs.com/del/archive/2007/12/13/993757.html
http://www.cnblogs.com/del/archive/2008/01/17/1042904.html
=====================================================================================
?
unit Unit5;interfaceusesWinapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;typeTForm5 = class(TForm)Button1: TButton;procedure Button1Click(Sender: TObject);private{ Private declarations }public{ Public declarations }end;/// <summary>/// 定義一個(gè) 人類/// </summary>TPerson = class(TObject)privateFname: string;Fage: Integer;procedure Setage(const Value: Integer);procedure Setname(const Value: string);public/// <summary>/// 1由于 TObject類的構(gòu)造方法 不是 虛方法 和 動(dòng)態(tài)方法, 所以不能 overide/// 所以重定義一個(gè)Create/// 標(biāo)準(zhǔn)寫(xiě)法1/// </summary>constructor Create;//overload; 若有多個(gè)重載,這里不要忘記加 overload/// <summary>/// 重載構(gòu)造方法, 標(biāo)準(zhǔn)寫(xiě)法2/// </summary>//constructor Create(const aName: string; const aAge: Integer); overload;/// <summary>/// 1.重寫(xiě)即overide析構(gòu)方法,由于tobject的析構(gòu)方法是個(gè)虛方法,但是比較特殊,/// 子類可以選擇是否重寫(xiě)(普通方法是不可以選擇的)////// 2.這樣寫(xiě)是重定義,雖然允許這樣玩,但是你要知道父類是個(gè)虛方法,盡量不要這樣寫(xiě)/// </summary>//destructor Destroy;/// <summary>/// 標(biāo)準(zhǔn)寫(xiě)法, 重寫(xiě)覆蓋父類的虛方法, 加上override 關(guān)鍵詞/// </summary>destructor Destroy; override;/// <summary>/// 重載析構(gòu)方法, 不要這樣玩, 因?yàn)槟阋? 我們通常釋放對(duì)象都是用 MyObj.Free;來(lái)/// 調(diào)用Destroy的,而Free是沒(méi)有參數(shù)的, 所以若你這么玩, 那么你必須釋放的時(shí)候這樣寫(xiě)/// MyObj.Destroy('a123') 且為了安全你還得與Free一致,方法體內(nèi)釋放前判斷下對(duì)象是否為nil/// 不如直接用Free來(lái)的簡(jiǎn)單,所以這種方法可以不用./// </summary>//destructor Destroy(a: string); overload;property name: string read Fname write Setname;property age: Integer read Fage write Setage;end;/// <summary>/// 定義一個(gè)人類的子類 婦女類/// </summary>TWoman = class(TPerson)public/// <summary>/// 重定義一個(gè)構(gòu)造方法,測(cè)試默認(rèn)不寫(xiě)inherited Create的時(shí)候,是否調(diào)用了父類的構(gòu)造方法/// 試驗(yàn)證明: inherited Create 不可省略, 不寫(xiě)的時(shí)候不調(diào)用父類的構(gòu)造函數(shù),這樣才是最/// 合理的。/// </summary>constructor Create;function makeLove(): string;end;varForm5: TForm5;implementation{$R *.dfm}{ TPerson }constructor TPerson.Create; begin//標(biāo)準(zhǔn)寫(xiě)法inherited Create; end;//constructor TPerson.Create(const aName: string; const aAge: Integer); //begin // inherited Create; // Fname := aName; // Fage := aAge; //end;//destructor TPerson.Destroy(a: string); //begin // //end;destructor TPerson.Destroy; beginend;procedure TPerson.Setage(const Value: Integer); beginFage := Value; end;procedure TPerson.Setname(const Value: string); beginFname := Value; end;procedure TForm5.Button1Click(Sender: TObject); varpp: TPerson;mm: TWoman; beginpp := TPerson.Create;mm := TWoman.Create;tryfinallypp.Free;mm.Free;end;end;{ TWoman }constructor TWoman.Create; begin//不寫(xiě)這句不調(diào)用父類的構(gòu)造函數(shù),所以還是寫(xiě)上標(biāo)準(zhǔn) 安全。inherited Create; end;function TWoman.makeLove: string; beginend;end.?
從哲學(xué)的角度講創(chuàng)建一個(gè)類的實(shí)例是這樣的;
創(chuàng)建走正序:父親.Create ----->> 兒子.Create ----->> 孫子.Create
銷毀走逆序:父親.Destroy?<<----- 兒子.Destroy <<----- 孫子.Destroy
即先有父親,父親把一些基本通用的成員屬性或方法初始化后 才能讓兒子繼承啊;沒(méi)有父親何來(lái)兒子呢;
所以一般不要把構(gòu)造函數(shù)Create弄成虛函數(shù)。這樣做也沒(méi)意義;構(gòu)造函數(shù) 一個(gè)類 重定義一個(gè)構(gòu)造函數(shù),子類的構(gòu)造函數(shù)中可以使用inherit
來(lái)調(diào)用父類的構(gòu)造函數(shù),一般構(gòu)造函數(shù)不會(huì)定義成虛函數(shù),即不允許 overide ;比如 TObject的構(gòu)造函數(shù)。
析構(gòu)函數(shù)一般都是弄成虛函數(shù),要求子類必須overide(雖然不overide也不報(bào)錯(cuò),但是你要養(yǎng)成良好的習(xí)慣,盡量這么做),這樣才能保證多態(tài)的
使用場(chǎng)景下,調(diào)用的是子類的析構(gòu)函數(shù),即:父類的實(shí)例 := 子類的.Create ;父類的實(shí)例.Free 依然是調(diào)用的
子類.Destroy 確保了;先銷毀子類的成員,再銷毀父類的成員。
?
unit Unit5;interfaceusesWinapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;typeTForm5 = class(TForm)Button1: TButton;procedure Button1Click(Sender: TObject);procedure FormCreate(Sender: TObject);private{ Private declarations }public{ Public declarations }end;/// <summary>/// 父親類/// </summary>TFather = classprivateFname: string;Flist1: TStringList;procedure Setname(const Value: string);procedure Setlist1(const Value: TStringList);public//若需要初始化的時(shí)候做一些特殊的事的話,那么重定義構(gòu)造函數(shù)constructor Create;//若需要銷毀的時(shí)候做一些特殊事的話,由于祖先類是虛函數(shù),那么父類子類都需要overide到底.destructor Destroy; override;property name: string read Fname write Setname;property list1: TStringList read Flist1 write Setlist1;end;/// <summary>/// 兒子類/// </summary>TSon = class(TFather)privateFage: Integer;Flist2: TStringList;procedure Setage(const Value: Integer);procedure Setlist2(const Value: TStringList);public//若需要初始化的時(shí)候做一些特殊的事的話,那么重定義構(gòu)造函數(shù)constructor Create;//若需要銷毀的時(shí)候做一些特殊事的話,由于祖先類是虛函數(shù),那么父類子類都需要overide到底.destructor Destroy; override;property age: Integer read Fage write Setage;property list2: TStringList read Flist2 write Setlist2;end;/// <summary>/// 孫子類/// </summary>TGrandson = class(TSon)privateFsex: Boolean;procedure Setsex(const Value: Boolean);public//若需要初始化的時(shí)候做一些特殊的事的話,那么重定義構(gòu)造函數(shù)constructor Create;//沒(méi)必要了,因?yàn)殇N毀的時(shí)候不需要做事//destructor Destroy; override;property sex: Boolean read Fsex write Setsex;//如果不需要銷毀的時(shí)候做一些事,比如這里我不定義一個(gè)TStringList,那么就沒(méi)有必要去重寫(xiě)父類的Destroy//property list3: TStringList read Flist3 write Setlist3;end;varForm5: TForm5;implementation{$R *.dfm}procedure TForm5.Button1Click(Sender: TObject); varsz: TGrandson; beginsz := TGrandson.Create;tryShowMessage(sz.name);ShowMessage(sz.list1.Text);finallysz.Free;end; end;{ TFather }constructor TFather.Create; begininherited Create;Self.Fname := '小李飛刀';Self.Flist1 := TStringList.Create;Self.Flist1.Add('111');OutputDebugString('父親'); end;{ TSon }constructor TSon.Create; begininherited Create;Self.Fage := 100;Self.Flist2 := TStringList.Create;Self.Flist2.Add('222');OutputDebugString('兒子'); end;{ TGrandson }constructor TGrandson.Create; begininherited Create;Self.sex := True;OutputDebugString('孫子'); end;destructor TFather.Destroy; beginSelf.Flist1.Free;inherited; end;procedure TFather.Setlist1(const Value: TStringList); beginFlist1 := Value; end;procedure TFather.Setname(const Value: string); beginFname := Value; end;destructor TSon.Destroy; beginSelf.Flist2.Free;inherited; end;procedure TSon.Setage(const Value: Integer); beginFage := Value; end;procedure TSon.Setlist2(const Value: TStringList); beginFlist2 := Value; end;procedure TGrandson.Setsex(const Value: Boolean); beginFsex := Value; end;procedure TForm5.FormCreate(Sender: TObject); beginReportMemoryLeaksOnShutdown := True; end;end.?
?
創(chuàng)建孫子類的實(shí)例的時(shí)候,會(huì)逐級(jí)先向上一直追溯到TObject.Create,先把繼承過(guò)來(lái)的逐漸初始化一遍,才能輪到自己的。
?
?以下是網(wǎng)上的摘抄,不一定正確,切圖:
?
?
我覺(jué)得這個(gè)人 說(shuō)的非常好,還是要看基類的,即祖先類。若祖先類 用了 virtual 那么無(wú)論是 構(gòu)造函數(shù) 還是 析構(gòu)函數(shù),若你需要在構(gòu)造和析構(gòu)的時(shí)候 做一些特殊的事的話,那么你必須overide ,子類繼續(xù)overide,overide到底。養(yǎng)成良好的編程習(xí)慣。
?
接下來(lái)我來(lái)舉個(gè)例子來(lái)說(shuō)明為什么,析構(gòu)函數(shù)要能弄成虛方法:
例子1,重定義Destroy然后用Free來(lái)釋放的話,那么會(huì)內(nèi)存泄露;
?
?
針對(duì)這個(gè)問(wèn)題,當(dāng)然有多重解決方案,比如重定義Free方法,或者釋放的時(shí)候用實(shí)例.Destroy ,然后為了安全 大不了 Destroy里 也判斷下 Self是否為nil; 但是這些解決方案都是把問(wèn)題 復(fù)雜化的方案了。何必不用overide呢。把父類的Destroy給覆蓋掉。不就好了。即使再父類調(diào)用Free;由于 是子類創(chuàng)建的實(shí)例,那么父類的Destroy也是被子類的覆蓋掉了的,那么就能保證TObject.Free;實(shí)際上是調(diào)用了T人類.Destroy,這樣就不會(huì)有內(nèi)存泄露了,這塊設(shè)計(jì)的復(fù)雜吧,一般人不深入研究根本不會(huì)明白,因?yàn)槲鰳?gòu)的時(shí)候,我們并沒(méi)有純純的使用Destroy,而是為了安全使用了Free; 而Free又是定義再父類的。
?
unit Unit5;interfaceusesWinapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;typeTForm5 = class(TForm)Button1: TButton;procedure Button1Click(Sender: TObject);procedure FormCreate(Sender: TObject);private{ Private declarations }public{ Public declarations }end;/// <summary>/// 人類/// </summary>TPerson = classprivateFlist1: TStringList;procedure Setlist1(const Value: TStringList);publicconstructor Create;//這里Destroy我選擇了重定義,沒(méi)有覆蓋父類TObject.Destroy//當(dāng)調(diào)用.Free來(lái)釋放的時(shí)候?qū)⒂肋h(yuǎn)不會(huì)執(zhí)行這個(gè)析構(gòu)函數(shù)destructor Destroy;property list1: TStringList read Flist1 write Setlist1;end;varForm5: TForm5;implementation{$R *.dfm}{ TPerson }constructor TPerson.Create; begininherited;Self.Flist1 := TStringList.Create;Self.Flist1.Add('111'); end;destructor TPerson.Destroy; beginSelf.Flist1.Free;inherited; end;procedure TPerson.Setlist1(const Value: TStringList); beginSelf.Flist1 := Value; end;procedure TForm5.Button1Click(Sender: TObject); varpp: TPerson; beginpp := TPerson.Create;tryShowMessage(pp.list1.Text);finally//這里根本就沒(méi)有調(diào)用我們上面聲明的Destroy,依然是調(diào)用父類的Destroy,//因?yàn)镕ree是Free是個(gè)普通的方法聲明父類,所以他依然是調(diào)用了父類的Destroy什么都沒(méi)有做//所以這里就會(huì)有內(nèi)存泄露 pp.Free;end; end;procedure TForm5.FormCreate(Sender: TObject); beginReportMemoryLeaksOnShutdown := True; end;end.?
?
?
?
?
?
這篇博客可以說(shuō)是百忙之中寫(xiě)出來(lái)的,由于這塊設(shè)計(jì)的邏輯 會(huì)有點(diǎn)繞;我來(lái)個(gè)結(jié)論吧:
1.構(gòu)造方法,如果子類需要加強(qiáng),或需要重載,那么就需要重定義;
2.析構(gòu)方法,如果子類需要加強(qiáng),那么就需要重寫(xiě)覆蓋overide父類的,且析構(gòu)方法一般不重載。
3.普通方法,如果子類需要加強(qiáng),那么就需要父類定義成虛方法,然后子類覆蓋overide;只有這樣才能做到多態(tài)的情況下使用。
?
?
?
2017-05-23 補(bǔ)充:
對(duì)于繼承組件的類 構(gòu)造方法必須覆蓋;
?
?
?
?
轉(zhuǎn)載于:https://www.cnblogs.com/del88/archive/2012/01/18/2325722.html
總結(jié)
以上是生活随笔為你收集整理的构造函数 Create 与 析构函数 Destroy的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: (hibernate之三)session
- 下一篇: java常用类解析十:Date类和Cal