版權(quán)說(shuō)明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡(jiǎn)介
1、Delphi 接口和編程兩大陷阱Delphi 接口和編程兩大陷阱 2012-06-01 00:20 150 人閱讀 評(píng)論 (1) 收藏舉報(bào)Delphi 接口編程的兩大陷阱前一陣寫(xiě)了一個(gè)通過(guò)接口擴(kuò)展功能的例子,當(dāng)時(shí)由于指針和 接口的轉(zhuǎn)換,導(dǎo)致了很多錯(cuò)誤,最近又接觸到了一個(gè)類和接 口混用的例子,導(dǎo)致程序的指針在傳遞中變了地址或者內(nèi) 容,導(dǎo)致讀到了錯(cuò)誤的地址,現(xiàn)在將接口和類之間的情況進(jìn) 行一下匯總。陷阱一、接口的類型轉(zhuǎn)換陷阱 1) 不能把一個(gè)對(duì)象引用強(qiáng)制轉(zhuǎn)換成這個(gè)引用的類型沒(méi) 有聲明實(shí)現(xiàn)的接口,即使這個(gè)對(duì)象實(shí)際實(shí)現(xiàn)了這個(gè)接口呵 呵,優(yōu)點(diǎn)拗口 。 2) 當(dāng)把一個(gè)對(duì)象變量賦給一個(gè)接口變量, 在把這個(gè)接
2、口變量賦還給對(duì)象變量時(shí),這個(gè)對(duì)象變量的地址已經(jīng)變了,也就是不再是原來(lái)的對(duì)象了,而是指向一個(gè)錯(cuò)誤的地址 例如:I1 = interfacefunction Do: Boolean;end;TC1 = classATT1: Integer;end;TC2 = Class(TC1, I1)ATT2: Integer;function Do: Boolean;end; Intf1: I1;OBJ1: TC!;OBJ2: TC2;OBJ2 := TC2.Create;OBJ1 := OBJ2.I1(OBJ2).DO; 正確。I1(OBJ1).DO; 編譯失敗因?yàn)?OBJ1 的類型 TC1 沒(méi)有聲明實(shí)現(xiàn)
3、I1 所以不能轉(zhuǎn)換成 I1 , 即使 OBJ1 確實(shí)實(shí)現(xiàn)了 I1。如果把對(duì)象轉(zhuǎn)為接口再轉(zhuǎn)回來(lái)也會(huì)有問(wèn)題。OBJ2 := TC2.Create;OBJ2.ATT1 := 0;Intf1 := OBJ2;/ 正確。TC2(Intf1).ATT1 := 0; / 運(yùn)行期非法地址訪問(wèn)錯(cuò)誤。OBJ2.ATT1 := 0; / 運(yùn)行期非法地址訪問(wèn)錯(cuò)誤。也就是,從對(duì)象引用轉(zhuǎn)換成指針引用后,地址改變了,但是 由指針引用再轉(zhuǎn)回對(duì)象引用時(shí)地址沒(méi)有變回來(lái) Delphi 的 bug?。陷阱二、接口的生存期管理 我認(rèn)為接口是不需要生存期管理的,因?yàn)榻涌诟静豢赡苌?成真正的對(duì)象。但是 Delphi 卻又一次打擊了我的常
4、識(shí)咦, 為什么要說(shuō)“又”呢? ,它的接口是有生存期的,而且必 須實(shí)現(xiàn)以下三個(gè)方法:function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;function _AddRef: Integer; stdcall;function _Release: Integer; stdcall;每次都要實(shí)現(xiàn)這三個(gè)方法是比較麻煩的,而且更重要的是,我不知道 Delphi 什么時(shí)候用以及怎么用這三個(gè)方法?所以 我也不知道怎么實(shí)現(xiàn)這三個(gè)方法。如果不想自己實(shí)現(xiàn)這三個(gè)方法,你可以使用TComponent 。因?yàn)?TComponent 已經(jīng)
5、實(shí)現(xiàn)了這三個(gè)方法,所以可以從它繼 承,就不用實(shí)現(xiàn)這三個(gè)方法了。這樣就可以放心使用了嗎?答案是否認(rèn)的。 因?yàn)?Delphi 在你 把接口變量置為 nil 時(shí)偷偷的因?yàn)楹艹龊跷业囊饬险{(diào)用 了 _Releaseofunction _IntfClear(var Dest: IInterface): Pointer;varP: Pointer;begin if Dest nil thenbeginP := Pointer(Dest);Pointer(Dest) := nil;IInterface(P)._Release;end;end;而Release 時(shí)又做了什么呢?function TCompone
6、nt._Release:Integer;beginif FVCLComObject = nil thenelseResult := IVCLComObject(FVCLComObject)._Release;end;不是 Com 對(duì)象的話,就什么也沒(méi)作。我們作的不是 Com 對(duì) 象,是不是就沒(méi)有任何問(wèn)題了呢?答案依然是否認(rèn)的,考慮 如下情況: OBJ2 := TC2.Create;tryIntf1 := OBJ2;Intf1.DO;FinallyOBJ2.Free;End;會(huì)怎么樣呢?會(huì)出非法地址訪問(wèn)錯(cuò)誤。為什么?上面說(shuō)過(guò)把 接口引用設(shè)為 nil 時(shí),會(huì)調(diào)用 _IntfClear ,而 _In
7、tfClear 又會(huì)調(diào) 用對(duì)象的Release,而這時(shí)這個(gè)對(duì)象已經(jīng)釋放了,自然就出 非法地址訪問(wèn)錯(cuò)誤啦。有人說(shuō)多此一舉嗎,接口引用只是個(gè)地址,沒(méi)必要手動(dòng)設(shè)為nil 。OBJ2 := TC2.Create;tryIntf1 := OBJ2;Intf1.DO;FinallyOBJ2.Free;End;結(jié)果可能還會(huì)出你的意料,還是非法地址訪問(wèn)錯(cuò)誤。為什 么?因?yàn)?Delphi 編譯器耍了個(gè)小聰明, 它認(rèn)為你忘記把這個(gè) 地址引用置為 nil 了,所以你會(huì)自動(dòng)給你加上,看來(lái) Delphi 編譯器聰明過(guò)頭了 J。怎么解決呢?方法 1,先把接口引用置為 nil ,再釋放對(duì)象。Intf1 := nil;OBJ
8、2.Free;方法 2,把接口引用強(qiáng)制轉(zhuǎn)成指針類型再置為nil 。Pointer(Intf1) := nil;此時(shí)相當(dāng)于直接把地址清零,不會(huì)調(diào)用 _IntfClear 。我傾向于使用第二種方法,這樣你就不用考慮先釋放誰(shuí)的問(wèn) 題了。而且有些設(shè)計(jì)模式中你可能只持有接口引用,而且你 也不知道引用的對(duì)象什么時(shí)候釋放, 此時(shí)就必須使用方法 2例如考慮 Composite 模式。TComposite = class(TComponent, I1)PrivateinterList: TXContainer;/ 一個(gè)容器類 ,存放“葉子”的接口引用PublicProcedure Add (AIntf: I1
9、;function DO: Boolean;End;它應(yīng)該釋放它的“葉子”嗎?顯然不是,那“葉子”是不是 一定會(huì)晚于這個(gè) “合成對(duì)象” 對(duì)象釋放呢?我想也不一定吧。 如果強(qiáng)制這樣規(guī)定的話,就失去很多的靈活性。所以我們肯 定想這些接口引用置 nil 時(shí),不會(huì)和原對(duì)象發(fā)生什么關(guān)系, 以免對(duì)象被釋放后出非法地址訪問(wèn)錯(cuò)誤??紤]使用什么容器 呢? array? TList ? TInterfaceList ?首先想到肯定是 TInterfaceList 了,因?yàn)槲覀兪且菁{的就是 接口。但是對(duì)他進(jìn)行 Free 時(shí),它會(huì)把它所有容納的接口置為 nil ,這正是我們不想要的。 或者我們可以在 Free 之前
10、先把它 存儲(chǔ)的接口引用轉(zhuǎn)為指針再置為 nil 。for I := 0 to interList.Count -1 doPointer(interList.Itemsi) := nil;可惜的是,編譯錯(cuò)誤“ Error XXXX.pas(XX): Left side cannot be assigned to”。然后我們?cè)囈幌?array。interList: Array of I1;動(dòng)態(tài)數(shù)組是不要釋放的。好似很好用,但是編譯器釋放它時(shí) 還是會(huì)對(duì)每個(gè)元素置為 nil 的,而且是作為接口,仍然有非 法地址訪問(wèn)錯(cuò)誤的可能??梢赃@樣for i := Low(arr) to High(arr) doPo
11、inter(arri) := nil;但是這畢竟是違反編碼習(xí)慣的,而且每次使用都要記得作, 不記得作也可能不會(huì)馬上出錯(cuò),所以有可能成為隱患。最后,就是使用 TList 。不過(guò) TList 中是指針, 所以 Add 時(shí)必 須這樣procedure XXX.Add(AIntf: I1)BeginInterList.Add(Pointer(AIntf);End;由于它本來(lái)就保存的是指針,所以釋放時(shí)也不需要特殊處理。好似比較完美,但是還是有一個(gè)陷阱,如果你寫(xiě)了這樣的代 碼會(huì)怎么樣呢?interList.Add(TC2.Create);或者Obj2 := TC2.Create;interList.Add
12、(Obj2);錯(cuò)!因?yàn)楸4娴氖羌兇獾闹羔?,所以轉(zhuǎn)化為接口時(shí),對(duì)象引用到接口引用的地址轉(zhuǎn)換沒(méi)有進(jìn)行它也不知道如何進(jìn)行所以調(diào)用接口聲明的方法時(shí)就又是一個(gè)非法地址訪問(wèn)錯(cuò)誤。 只能這么寫(xiě):interList.Add(Pointer(I1(TC2.Create);雖然有些麻煩, 相比之下這已是最正確方案 我所知的 了因?yàn)槟闳绻阃涋D(zhuǎn)會(huì)在第一次調(diào)用時(shí)就出錯(cuò),比較容易發(fā) 現(xiàn)錯(cuò)誤相比于使用 array。1、使用 Tlist 來(lái)管理接口引用。2、增加時(shí)要把對(duì)象轉(zhuǎn)型成 Interface 再轉(zhuǎn)型成 Pointer, 如果本來(lái)就是接口引用的話直接轉(zhuǎn)為 Pointer 即可。3、對(duì)于沒(méi)有使用 Tlist 來(lái)管理的接
13、口引用。對(duì)于接口的 引用要用下面的方法手動(dòng)置為nil : Pointer IntfRef := nil;另外,TlnterfacedObject也實(shí)現(xiàn)了 llnterface的三個(gè)方法。所以從它繼承也可以省去自己實(shí)現(xiàn)這三個(gè)方法的麻煩。但是它的_Release是這樣實(shí)現(xiàn)的:function TlnterfacedObject._Release: lnteger;beginResult := lnterlockedDecrement(FRefCount);if Result = 0 thenDestroy;end;接口引用的置nil會(huì)調(diào)用接口的Release,所以有可能會(huì)把對(duì) 象給釋放掉,我當(dāng)時(shí)可是被它嚇了一跳,多虧我沒(méi)用過(guò)它。 也就是這樣實(shí)現(xiàn)下比從 TComponent 繼承帶來(lái)更大的麻煩。 除非特殊用途,不建議使用。上面對(duì)接口的所有討論,只限于普通使用的語(yǔ)言級(jí)的接口, 沒(méi)有討論 Com+
溫馨提示
- 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 2025年華能內(nèi)蒙古東部能源有限公司招聘高校畢業(yè)生備考題庫(kù)帶答案詳解
- 2025年湖北省孝感市事業(yè)單位人才引進(jìn)秋季校園招聘879備考題庫(kù)及答案詳解1套
- 2025年重慶市沙坪壩區(qū)巴縣師范附屬小學(xué)校招聘數(shù)學(xué)代課教師備考題庫(kù)及答案詳解參考
- 普陀區(qū)教育系統(tǒng)2026年公開(kāi)招聘教師的備考題庫(kù)含答案詳解
- 2026年威海市環(huán)翠區(qū)民兵訓(xùn)練基地公開(kāi)招聘事業(yè)單位工作人員備考題庫(kù)及答案詳解一套
- 2025年河池市國(guó)有資本投資運(yùn)營(yíng)(集團(tuán))有限公司公開(kāi)招聘?jìng)淇碱}庫(kù)及參考答案詳解一套
- 2025年三穗縣融媒體中心公開(kāi)招聘5名臨聘人員備考題庫(kù)參考答案詳解
- 成都市泡桐樹(shù)小學(xué)天府智造園分校2025年儲(chǔ)備教師招聘?jìng)淇碱}庫(kù)及一套完整答案詳解
- 紹興市職業(yè)教育中心(紹興技師學(xué)院)2025學(xué)年第一學(xué)期第五次編外用工公開(kāi)招聘?jìng)淇碱}庫(kù)及完整答案詳解1套
- 2025年福建華南女子職業(yè)學(xué)院冬季人才招聘10人備考題庫(kù)及1套完整答案詳解
- 2025陜煤集團(tuán)神南產(chǎn)業(yè)發(fā)展有限公司社會(huì)招聘(120人)參考筆試試題及答案解析
- 不良事件上報(bào)中的“非懲罰性”文化推廣策略研究
- 2026年山西省政府采購(gòu)從業(yè)人員核心備考題庫(kù)(含典型題、重點(diǎn)題)
- 2026浙江大學(xué)黨政管理人員、專職輔導(dǎo)員和行政專員招聘80人考試筆試備考試題及答案解析
- 2025年考研英語(yǔ)閱讀理解專項(xiàng)訓(xùn)練(附答案)
- 無(wú)人機(jī)打藥合同范本
- 已婚男人分手協(xié)議書(shū)
- 成人失禁相關(guān)性皮炎的預(yù)防與護(hù)理試題及答案
- 2025年GCP考試題庫(kù)及答案(網(wǎng)校專用)
- 2025年社區(qū)警務(wù)規(guī)范考試題庫(kù)及答案
- 車輛和駕駛?cè)斯芾砟M試題及答案
評(píng)論
0/150
提交評(píng)論