




版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡介
1、Delphi 接口和編程兩大陷阱Delphi 接口和編程兩大陷阱 2012-06-01 00:20 150 人閱讀 評(píng)論 (1) 收藏舉報(bào)Delphi 接口編程的兩大陷阱前一陣寫了一個(gè)通過接口擴(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è)引用的類型沒 有聲明實(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)變了,也就是不再是原來的對(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 沒有聲明實(shí)現(xiàn)
3、I1 所以不能轉(zhuǎn)換成 I1 , 即使 OBJ1 確實(shí)實(shí)現(xiàn)了 I1。如果把對(duì)象轉(zhuǎn)為接口再轉(zhuǎn)回來也會(huì)有問題。OBJ2 := TC2.Create;OBJ2.ATT1 := 0;Intf1 := OBJ2;/ 正確。TC2(Intf1).ATT1 := 0; / 運(yùn)行期非法地址訪問錯(cuò)誤。OBJ2.ATT1 := 0; / 運(yùn)行期非法地址訪問錯(cuò)誤。也就是,從對(duì)象引用轉(zhuǎn)換成指針引用后,地址改變了,但是 由指針引用再轉(zhuǎn)回對(duì)象引用時(shí)地址沒有變回來 Delphi 的 bug?。陷阱二、接口的生存期管理 我認(rèn)為接口是不需要生存期管理的,因?yàn)榻涌诟静豢赡苌?成真正的對(duì)象。但是 Delphi 卻又一次打擊了我的常
4、識(shí)咦, 為什么要說“又”呢? ,它的接口是有生存期的,而且必 須實(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ì)象的話,就什么也沒作。我們作的不是 Com 對(duì) 象,是不是就沒有任何問題了呢?答案依然是否認(rèn)的,考慮 如下情況: OBJ2 := TC2.Create;tryIntf1 := OBJ2;Intf1.DO;FinallyOBJ2.Free;End;會(huì)怎么樣呢?會(huì)出非法地址訪問錯(cuò)誤。為什么?上面說過把 接口引用設(shè)為 nil 時(shí),會(huì)調(diào)用 _IntfClear ,而 _In
7、tfClear 又會(huì)調(diào) 用對(duì)象的Release,而這時(shí)這個(gè)對(duì)象已經(jīng)釋放了,自然就出 非法地址訪問錯(cuò)誤啦。有人說多此一舉嗎,接口引用只是個(gè)地址,沒必要手動(dòng)設(shè)為nil 。OBJ2 := TC2.Create;tryIntf1 := OBJ2;Intf1.DO;FinallyOBJ2.Free;End;結(jié)果可能還會(huì)出你的意料,還是非法地址訪問錯(cuò)誤。為什 么?因?yàn)?Delphi 編譯器耍了個(gè)小聰明, 它認(rèn)為你忘記把這個(gè) 地址引用置為 nil 了,所以你會(huì)自動(dòng)給你加上,看來 Delphi 編譯器聰明過頭了 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 。我傾向于使用第二種方法,這樣你就不用考慮先釋放誰的問 題了。而且有些設(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ì)象被釋放后出非法地址訪問錯(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 的,而且是作為接口,仍然有非 法地址訪問錯(cuò)誤的可能??梢赃@樣for i := Low(arr) to High(arr) doPo
11、inter(arri) := nil;但是這畢竟是違反編碼習(xí)慣的,而且每次使用都要記得作, 不記得作也可能不會(huì)馬上出錯(cuò),所以有可能成為隱患。最后,就是使用 TList 。不過 TList 中是指針, 所以 Add 時(shí)必 須這樣procedure XXX.Add(AIntf: I1)BeginInterList.Add(Pointer(AIntf);End;由于它本來就保存的是指針,所以釋放時(shí)也不需要特殊處理。好似比較完美,但是還是有一個(gè)陷阱,如果你寫了這樣的代 碼會(huì)怎么樣呢?interList.Add(TC2.Create);或者Obj2 := TC2.Create;interList.Add
12、(Obj2);錯(cuò)!因?yàn)楸4娴氖羌兇獾闹羔?,所以轉(zhuǎn)化為接口時(shí),對(duì)象引用到接口引用的地址轉(zhuǎn)換沒有進(jìn)行它也不知道如何進(jìn)行所以調(diào)用接口聲明的方法時(shí)就又是一個(gè)非法地址訪問錯(cuò)誤。 只能這么寫:interList.Add(Pointer(I1(TC2.Create);雖然有些麻煩, 相比之下這已是最正確方案 我所知的 了因?yàn)槟闳绻阃涋D(zhuǎn)會(huì)在第一次調(diào)用時(shí)就出錯(cuò),比較容易發(fā) 現(xiàn)錯(cuò)誤相比于使用 array。1、使用 Tlist 來管理接口引用。2、增加時(shí)要把對(duì)象轉(zhuǎn)型成 Interface 再轉(zhuǎn)型成 Pointer, 如果本來就是接口引用的話直接轉(zhuǎn)為 Pointer 即可。3、對(duì)于沒有使用 Tlist 來管理的接
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í)可是被它嚇了一跳,多虧我沒用過它。 也就是這樣實(shí)現(xiàn)下比從 TComponent 繼承帶來更大的麻煩。 除非特殊用途,不建議使用。上面對(duì)接口的所有討論,只限于普通使用的語言級(jí)的接口, 沒有討論 Com+
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(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年洪江市法院系統(tǒng)招聘真題
- 2025廣東韶關(guān)市南雄市司法局招聘1人模擬試卷及答案詳解(奪冠系列)
- 2025江蘇省退役軍人事務(wù)廳直屬優(yōu)撫醫(yī)院招聘12人考前自測(cè)高頻考點(diǎn)模擬試題及完整答案詳解一套
- 2025內(nèi)蒙古省際勞務(wù)協(xié)作招聘崗位考前自測(cè)高頻考點(diǎn)模擬試題及答案詳解(典優(yōu))
- 2025杭州醫(yī)學(xué)院招聘1人考前自測(cè)高頻考點(diǎn)模擬試題及答案詳解(易錯(cuò)題)
- 2025廣西科技大學(xué)招聘附屬醫(yī)院(臨床醫(yī)學(xué)院)領(lǐng)導(dǎo)干部3人模擬試卷及參考答案詳解一套
- 2025年雙門轎跑車合作協(xié)議書
- 2025河南新鄉(xiāng)育才高級(jí)中學(xué)新鄉(xiāng)市育才實(shí)驗(yàn)學(xué)校招聘70人考前自測(cè)高頻考點(diǎn)模擬試題及答案詳解一套
- 2025廣西河池市計(jì)量測(cè)試研究所招聘工作人員2人模擬試卷(含答案詳解)
- 2025廣西玉林容縣公安局第一次公開招聘警務(wù)輔助人員23人考前自測(cè)高頻考點(diǎn)模擬試題及答案詳解(名校卷)
- 江浙皖高中(縣中)發(fā)展共同體2025-2026學(xué)年高三上學(xué)期10月聯(lián)考物理試題(含答案)
- 資陽發(fā)展投資集團(tuán)有限公司第二輪一般員工市場(chǎng)化招聘筆試歷年參考題庫附帶答案詳解
- 微納集成電路制造工藝 課件全套 第1-12章 緒論;硅單晶與硅晶圓制備工藝 -工藝集成與工藝流程
- 心理健康教育課程名詞解釋大全
- 廣東電網(wǎng)公司海南電網(wǎng)公司南網(wǎng)能源公司2025年9月社會(huì)招聘筆試參考題庫附帶答案詳解
- 發(fā)酵車間崗前安全培訓(xùn)課件
- 開學(xué)第一課【快閃】浪浪山小妖怪:誰都可以從現(xiàn)在開始
- 2025成人高考專升本政治試題及答案
- 慢阻肺臨床路徑試題及答案
- 800個(gè)產(chǎn)糧大縣名單
- 2025年新兼職安全員安全培訓(xùn)試題及答案
評(píng)論
0/150
提交評(píng)論