亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > Delphi > 正文

Delphi的接口陷阱

2019-11-18 18:16:40
字體:
來源:轉載
供稿:網友

Delphi的接口陷阱

現在我所知的有兩大陷阱:

陷阱一、接口的類型轉換陷阱

a)       不能把一個對象引用強制轉換成這個引用的類型沒有聲明實現的接口,即使這個對象實際實現了這個接口(呵呵,優點拗口)。
b)       當把一個對象變量賦給一個接口變量,在把這個接口變量賦還給對象變量時,這個對象變量的地址已經變了,也就是不再是原來的對象了,而是指向一個錯誤的地址。
例如:
I1 = interface
    function Do: Boolean;
end;

TC1 = Class
    ATT1: 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;編譯失敗。
因為OBJ1的類型TC1沒有聲明實現I1所以不能轉換成I1,即使OBJ1確實實現了I1。
還有,如果把對象轉為接口再轉回來也會有問題。
OBJ2 := TC2.Create;
OBJ2.ATT1 := 0;
Intf1 := OBJ2;//正確。
OBJ2 := Intf1;
TC2(Intf1).ATT1 := 0; //運行期非法地址訪問錯誤。
OBJ2.ATT1 := 0; //運行期非法地址訪問錯誤。
也就是,從對象引用轉換成指針引用后,地址改變了,但是由指針引用再轉回對象引用時地址沒有變回來(Delphi的bug?)。
 

陷阱二、接口的生存期管理

依據我的常識(此處是編程常識,不是Delphi使用常識)來講,我認為接口是不需要生存期管理的,因為接口根本不可能生成真正的對象。但是Delphi卻又一次打擊了我的常識(咦,為什么要說“又”呢?),它的接口是有生存期的,而且必須實現以下三個方法:
    function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
    function _AddRef: Integer; stdcall;
function _Release: Integer; stdcall;
每次都要實現這三個方法是比較麻煩的,而且更重要的是,我不知道Delphi什么時候用以及怎么用這三個方法?所以我也不知道怎么實現這三個方法。J
如果不想自己實現這三個方法,你可以使用TComponent。因為TComponent已經實現了這三個方法,所以可以從它繼承,就不用實現這三個方法了。
這樣就可以放心使用了嗎?答案是否定的。因為Delphi在你把接口變量置為nil時偷偷的(因為很出乎我的意料)調用了_Release。
function _IntfClear(var Dest: IInterface): Pointer;
var
  P: Pointer;
begin
  Result := @Dest;
  if Dest <> nil then
  begin
    P := Pointer(Dest);
    Pointer(Dest) := nil;
    IInterface(P)._Release;
  end;
end;
而_Release時又做了什么呢?
function TComponent._Release: Integer;
begin
  if FVCLComObject = nil then
    Result := -1   // -1 indicates no reference counting is taking place
  else
    Result := IVCLComObject(FVCLComObject)._Release;
end;
不是Com對象的話,就什么也沒作。我們作的不是Com對象,是不是就沒有任何問題了呢?答案依然是否定的,考慮如下情況:
OBJ2 := TC2.Create;
try
Intf1 := OBJ2;
Intf1.DO;
Finally
    OBJ2.Free;
    Intf1 := nil;
End;
會怎么樣呢?會出非法地址訪問錯誤。為什么?上面說過把接口引用設為nil時,會調用_IntfClear,而_IntfClear又會調用對象的_Release,而這時這個對象已經釋放了,自然就出非法地址訪問錯誤啦。
有人說多此一舉嗎,接口引用只是個地址,沒必要手動設為nil。
OBJ2 := TC2.Create;
try
Intf1 := OBJ2;
Intf1.DO;
Finally
    OBJ2.Free;
End;
結果可能還會出你的意料,還是非法地址訪問錯誤。為什么?因為Delphi編譯器耍了個小聰明,它認為你忘記把這個地址引用置為nil了,所以你會自動給你加上,看來Delphi編譯器聰明過頭了J。
怎么解決呢?
方法1,先把接口引用置為nil,再釋放對象。
    Intf1 := nil;
    OBJ2.Free;
方法2,把接口引用強制轉成指針類型再置為nil。
    Pointer(Intf1) := nil;
此時相當于直接把地址清零,不會調用_IntfClear。
我傾向于使用第二種方法,這樣你就不用考慮先釋放誰的問題了。而且有些設計模式中你可能只持有接口引用,而且你也不知道引用的對象什么時候釋放,此時就必須使用方法2。
例如考慮Composite模式。
TComposite = class(TComponent, I1)
PRivate
    interList: TXContainer;//一個容器類,存放“葉子”的接口引用。
Public
    Procedure Add (AIntf: I1);
    function DO: Boolean;
End;
它應該釋放它的“葉子”嗎?顯然不是,那“葉子”是不是一定會晚于這個“合成對象”對象釋放呢?我想也不一定吧。如果強制這樣規定的話,就失去很多的靈活性。所以我們肯定想這些接口引用置nil時,不會和原對象發生什么關系,以免對象被釋放后出非法地址訪問錯誤。考慮使用什么容器呢?array?TList?TInterfaceList?
首先想到肯定是TInterfaceList了,因為我們是要容納的就是接口。但是對他進行Free時,它會把它所有容納的接口置為nil,這正是我們不想要的?;蛘呶覀兛梢栽贔ree之前先把它存儲的接口引用轉為指針再置為nil。
  for I := 0 to interList.Count -1 do
Pointer(interList.Items[i]) := nil;
可惜的是,編譯錯誤“[Error] XXXX.pas(XX): Left side cannot be assigned to”。
然后我們試一下array。
interList: Array of I1;
動態數組是不要釋放的。好像很好用,但是編譯器釋放它時還是會對每個元素置為nil的,而且是作為接口,仍然有非法地址訪問錯誤的可能。可以這樣
  for i := Low(arr) to High(arr) do
Pointer(arr[i]) := nil;
但是這畢竟是違反編碼習慣的,而且每次使用都要記得作,不記得作也可能不會馬上出錯,所以有可能成為隱患。
最后,就是使用TList。不過TList中是指針,所以Add時必須這樣
procedure XXX.Add(AIntf: I1)
Begin
    InterList.Add(Pointer(AIntf));
End;
由于它本來就保存的是指針,所以釋放時也不需要特殊處理。
好像比較完美,但是還是有一個陷阱,如果你寫了這樣的代碼會怎么樣呢?
interList.Add(TC2.Create);
或者
Obj2 := TC2.Create;
interList.Add(Obj2);
錯!因為保存的是純粹的指針,所以轉化為接口時,對象引用到接口引用的地址轉換沒有進行(它也不知道如何進行),所以調用接口聲明的方法時就又是一個非法地址訪問錯誤。只能這么寫:
interList.Add(Pointer(I1(TC2.Create)));
雖然有些麻煩,相比之下這已是最佳方案(我所知的)了。因為你如果你忘記轉會在第一次調用時就出錯,比較容易發現錯誤(相比于使用array)。
所以我建議:
1,  使用Tlist來管理接口引用。
2,  增加時要把對象轉型成Interface再轉型成Pointer,如果本來就是接口引用的話直接轉為Pointer即可。
3,  對于沒有使用Tlist來管理的接口引用。對于接口的引用要用下面的方法手動置為nil:Pointer(IntfRef):= nil;
另外,TInterfacedObject也實現了IInterface的三個方法。所以從它繼承也可以省去自己實現這三個方法的麻煩。但是它的_Release是這樣實現的:
function TInterfacedObject._Release: Integer;
begin
  Result := InterlockedDecrement(FRefCount);
  if Result = 0 then
    Destroy;
end;
我們前邊說過,接口引用的置nil會調用接口的_Release,所以有可能會把對象給釋放掉,我當時可是被它嚇了一跳,多虧我沒用過它。也就是這樣實現下比從TComponent繼承帶來更大的麻煩。除非特殊用途,不建議使用。
上面對接口的所有討論,只限于普通使用的語言級的接口,沒有討論Com+接口。Delphi的這些詭異的接口的實現,和它是從Com+接口發展而來是有很大關系的,也就是由于背負了過重的歷史包袱而作出的妥協的結果。

上一篇:用DELPHI實現文件加密壓縮

下一篇:Delphi控制Excel的重要屬性和方法

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
學習交流
熱門圖片

新聞熱點

疑難解答

圖片精選

網友關注

亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
最近2019年好看中文字幕视频| 精品自拍视频在线观看| 国产一区二区三区在线看| 日韩黄色高清视频| 国产精品福利久久久| 国产精品96久久久久久| 欧美成人手机在线| 黄网站色欧美视频| 日韩成人在线播放| 久99久在线视频| 国产综合视频在线观看| 久久久精品久久久| 最近2019中文字幕在线高清| 成人激情视频免费在线| 亚洲国产黄色片| 日韩在线视频线视频免费网站| 亚洲一区二区久久久久久| 成人久久精品视频| 亚洲午夜精品久久久久久久久久久久| www高清在线视频日韩欧美| 亚洲电影免费观看高清完整版| 亚洲欧美日韩综合| 91精品视频专区| 中国china体内裑精亚洲片| 97热精品视频官网| 九九九久久国产免费| 国产成人高清激情视频在线观看| 国产日韩欧美在线视频观看| 在线国产精品播放| 欧美与黑人午夜性猛交久久久| 亚洲免费av片| 在线观看成人黄色| 亚洲色图综合久久| 欧美日韩免费区域视频在线观看| 午夜精品久久久久久久男人的天堂| 欧美有码在线观看| 8090成年在线看片午夜| 日韩视频免费中文字幕| 一区二区日韩精品| 久久国产精品网站| 97人人爽人人喊人人模波多| 欧美一级电影免费在线观看| 亚洲人成在线一二| 欧美一级片在线播放| 成人福利在线视频| 国产精自产拍久久久久久| 欧美多人爱爱视频网站| 菠萝蜜影院一区二区免费| 992tv成人免费视频| 亚洲bt天天射| 亚洲一级片在线看| 精品福利免费观看| 九九久久精品一区| 久久久久99精品久久久久| 国产精品美女www爽爽爽视频| 亚洲精品视频在线观看视频| 在线看欧美日韩| 欧美性极品xxxx娇小| 久久视频中文字幕| 国产视频亚洲视频| 国产精品国产三级国产专播精品人| 欧美在线视频一区二区| 911国产网站尤物在线观看| 97在线视频免费播放| 国产精品久久久久久五月尺| 国产日韩在线看片| 久久久久北条麻妃免费看| 国产精品第一视频| 久久人人爽人人爽人人片亚洲| 91久久在线视频| 国产精品96久久久久久又黄又硬| 亚洲一级黄色片| 欧美精品18videos性欧美| 国产精品视频网| 国产精品美女在线观看| 26uuu久久噜噜噜噜| 成人美女av在线直播| 中文字幕在线国产精品| 欧美日韩国产va另类| 久久精品中文字幕免费mv| 欧美另类交人妖| 尤物九九久久国产精品的分类| www.久久久久| 免费99精品国产自在在线| 精品成人乱色一区二区| 日韩免费高清在线观看| 国产三级精品网站| 欧美国产精品va在线观看| 亚洲系列中文字幕| 福利一区视频在线观看| 国产精品狠色婷| 这里精品视频免费| 久久国产精品影片| 亚洲电影免费观看| 成人免费视频a| 亚洲国产欧美一区二区三区久久| 在线观看欧美日韩国产| 亚洲精品乱码久久久久久金桔影视| 91手机视频在线观看| 国产精品96久久久久久又黄又硬| 亚洲男人第一网站| 久久综合免费视频影院| 亚洲成人性视频| 欧美激情按摩在线| 亚洲精品美女在线| 精品亚洲男同gayvideo网站| 热久久视久久精品18亚洲精品| 日韩av黄色在线观看| 日韩女优人人人人射在线视频| 夜夜嗨av色一区二区不卡| 国产精品网站视频| 中文字幕日韩专区| 国产精品69久久| 日韩毛片中文字幕| 亚洲最大的成人网| 中文字幕日本欧美| 欧美视频中文在线看| 粉嫩av一区二区三区免费野| 欧美性受xxxx黑人猛交| 国产精品久久久久久久久久小说| 日韩av在线网站| 日韩经典第一页| 久久久久久网站| 亚洲精品美女视频| 中文字幕在线观看日韩| 亚洲电影免费观看高清完整版在线观看| 亚洲男人天天操| 国产成人精品免高潮费视频| 久久色在线播放| 久久久久久国产免费| 国产在线播放不卡| 亚洲成人激情图| 欧美黄色片视频| 国产欧美日韩视频| 亚洲春色另类小说| 成人欧美一区二区三区黑人孕妇| 精品国产欧美成人夜夜嗨| 成人福利免费观看| 中文字幕日韩欧美精品在线观看| 全球成人中文在线| 成人在线播放av| 欧美老女人在线视频| 欧美久久精品一级黑人c片| 韩国日本不卡在线| 亚洲va欧美va在线观看| 日韩综合视频在线观看| 国产精品久久久久久久久久久久| 亚洲精品福利在线| 国产日韩欧美电影在线观看| 国产精品91视频| 久久久精品亚洲| 亚洲xxx自由成熟| 欧美人在线观看| 亚洲第一在线视频| 高清一区二区三区日本久| 国产精品久久久久久久久久久不卡| 国产精品国模在线| 亚洲欧美综合另类中字| 日韩国产精品一区| 精品久久久久久亚洲国产300| 黄网站色欧美视频| 久久69精品久久久久久国产越南| 亚洲国产精品小视频| 日韩在线视频播放|