狠狠爱成人网_日韩一级在线_国产综合自拍_亚洲精品韩国_亚洲视频导航_麻豆成人在线播放_欧美jjzz_一区在线视频观看_美脚丝袜一区二区三区在线观看_欧美91视频

當(dāng)前位置:系統(tǒng)之家 > 技術(shù)開發(fā)教程 > 詳細(xì)頁面

Delphi接口編程的2大陷阱

Delphi接口編程的2大陷阱

更新時間:2019-09-24 文章作者:未知 信息來源:網(wǎng)絡(luò) 閱讀次數(shù):

  陷阱一、接口的類型轉(zhuǎn)換陷阱

  a) 不能把一個對象引用強制轉(zhuǎn)換成這個引用的類型沒有聲明實現(xiàn)的接口,即使這個對象實際實現(xiàn)了這個接口(呵呵,優(yōu)點拗口)。

  b) 當(dāng)把一個對象變量賦給一個接口變量,在把這個接口變量賦還給對象變量時,這個對象變量的地址已經(jīng)變了,也就是不再是原來的對象了,而是指向一個錯誤的地址。

  例如:

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沒有聲明實現(xiàn)I1所以不能轉(zhuǎn)換成I1,即使OBJ1確實實現(xiàn)了I1。

  還有,如果把對象轉(zhuǎn)為接口再轉(zhuǎn)回來也會有問題。

OBJ2 := TC2.Create;

OBJ2.ATT1 := 0;
Intf1 := OBJ2;//正確。

OBJ2 := Intf1;

TC2(Intf1).ATT1 := 0; //運行期非法地址訪問錯誤。

OBJ2.ATT1 := 0; //運行期非法地址訪問錯誤。

  也就是,從對象引用轉(zhuǎn)換成指針引用后,地址改變了,但是由指針引用再轉(zhuǎn)回對象引用時地址沒有變回來(Delphi的bug?)。

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

  依據(jù)我的常識(此處是編程常識,不是Delphi使用常識)來講,我認(rèn)為接口是不需要生存期管理的,因為接口根本不可能生成真正的對象。但是Delphi卻又一次打擊了我的常識(咦,為什么要說“又”呢?),它的接口是有生存期的,而且必須實現(xiàn)以下三個方法:

function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
function _AddRef: Integer; stdcall;
function _Release: Integer; stdcall;

  每次都要實現(xiàn)這三個方法是比較麻煩的,而且更重要的是,我不知道Delphi什么時候用以及怎么用這三個方法?所以我也不知道怎么實現(xiàn)這三個方法。

  如果不想自己實現(xiàn)這三個方法,你可以使用TComponent。因為TComponent已經(jīng)實現(xiàn)了這三個方法,所以可以從它繼承,就不用實現(xiàn)這三個方法了。

  這樣就可以放心使用了嗎?答案是否定的。因為Delphi在你把接口變量置為nil時偷偷的(因為很出乎我的意料)調(diào)用了_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;

  會怎么樣呢?會出非法地址訪問錯誤。為什么?上面說過把接口引用設(shè)為nil時,會調(diào)用_IntfClear,而_IntfClear又會調(diào)用對象的_Release,而這時這個對象已經(jīng)釋放了,自然就出非法地址訪問錯誤啦。

  有人說多此一舉嗎,接口引用只是個地址,沒必要手動設(shè)為nil。

OBJ2 := TC2.Create;

try

Intf1 := OBJ2;

Intf1.DO;

Finally

OBJ2.Free;

End;

  結(jié)果可能還會出你的意料,還是非法地址訪問錯誤。為什么?因為Delphi編譯器耍了個小聰明,它認(rèn)為你忘記把這個地址引用置為nil了,所以你會自動給你加上,看來Delphi編譯器聰明過頭了J。

  怎么解決呢?

  方法1,先把接口引用置為nil,再釋放對象。

Intf1 := nil;
OBJ2.Free;

  方法2,把接口引用強制轉(zhuǎn)成指針類型再置為nil。

Pointer(Intf1) := nil;

  此時相當(dāng)于直接把地址清零,不會調(diào)用_IntfClear。

  我傾向于使用第二種方法,這樣你就不用考慮先釋放誰的問題了。而且有些設(shè)計模式中你可能只持有接口引用,而且你也不知道引用的對象什么時候釋放,此時就必須使用方法2。

  例如考慮Composite模式。

TComposite = class(TComponent, I1)
Private
interList: TXContainer;//一個容器類,存放“葉子”的接口引用。
Public
Procedure Add (AIntf: I1);
function DO: Boolean;
End;

  它應(yīng)該釋放它的“葉子”嗎?顯然不是,那“葉子”是不是一定會晚于這個“合成對象”對象釋放呢?我想也不一定吧。如果強制這樣規(guī)定的話,就失去很多的靈活性。所以我們肯定想這些接口引用置nil時,不會和原對象發(fā)生什么關(guān)系,以免對象被釋放后出非法地址訪問錯誤。考慮使用什么容器呢?array?TList?TInterfaceList?

  首先想到肯定是TInterfaceList了,因為我們是要容納的就是接口。但是對他進行Free時,它會把它所有容納的接口置為nil,這正是我們不想要的。或者我們可以在Free之前先把它存儲的接口引用轉(zhuǎn)為指針再置為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;

  動態(tài)數(shù)組是不要釋放的。好像很好用,但是編譯器釋放它時還是會對每個元素置為nil的,而且是作為接口,仍然有非法地址訪問錯誤的可能。可以這樣

for i := Low(arr) to High(arr) do
Pointer(arr[i]) := nil;

  但是這畢竟是違反編碼習(xí)慣的,而且每次使用都要記得作,不記得作也可能不會馬上出錯,所以有可能成為隱患。

  最后,就是使用TList。不過TList中是指針,所以Add時必須這樣

procedure XXX.Add(AIntf: I1)
Begin
 InterList.Add(Pointer(AIntf));
End;

  由于它本來就保存的是指針,所以釋放時也不需要特殊處理。

  好像比較完美,但是還是有一個陷阱,如果你寫了這樣的代碼會怎么樣呢?

interList.Add(TC2.Create);

  或者

Obj2 := TC2.Create;
interList.Add(Obj2);

  錯!因為保存的是純粹的指針,所以轉(zhuǎn)化為接口時,對象引用到接口引用的地址轉(zhuǎn)換沒有進行(它也不知道如何進行),所以調(diào)用接口聲明的方法時就又是一個非法地址訪問錯誤。只能這么寫:

interList.Add(Pointer(I1(TC2.Create)));

  雖然有些麻煩,相比之下這已是最佳方案(我所知的)了。因為你如果你忘記轉(zhuǎn)會在第一次調(diào)用時就出錯,比較容易發(fā)現(xiàn)錯誤(相比于使用array)。

  所以我建議:

  1、使用Tlist來管理接口引用。

  2、增加時要把對象轉(zhuǎn)型成Interface再轉(zhuǎn)型成Pointer,如果本來就是接口引用的話直接轉(zhuǎn)為Pointer即可。

  3、對于沒有使用Tlist來管理的接口引用。對于接口的引用要用下面的方法手動置為nil:Pointer(IntfRef):= nil;

  另外,TInterfacedObject也實現(xiàn)了IInterface的三個方法。所以從它繼承也可以省去自己實現(xiàn)這三個方法的麻煩。但是它的_Release是這樣實現(xiàn)的:

function TInterfacedObject._Release: Integer;
begin
 Result := InterlockedDecrement(FRefCount);
 if Result = 0 then
  Destroy;
end;

  我們前邊說過,接口引用的置nil會調(diào)用接口的_Release,所以有可能會把對象給釋放掉,我當(dāng)時可是被它嚇了一跳,多虧我沒用過它。也就是這樣實現(xiàn)下比從TComponent繼承帶來更大的麻煩。除非特殊用途,不建議使用。

  上面對接口的所有討論,只限于普通使用的語言級的接口,沒有討論Com+接口。Delphi的這些詭異的接口的實現(xiàn),和它是從Com+接口發(fā)展而來是有很大關(guān)系的,也就是由于背負(fù)了過重的歷史包袱而作出的妥協(xié)的結(jié)果。

溫馨提示:喜歡本站的話,請收藏一下本站!

本類教程下載

系統(tǒng)下載排行

狠狠爱成人网_日韩一级在线_国产综合自拍_亚洲精品韩国_亚洲视频导航_麻豆成人在线播放_欧美jjzz_一区在线视频观看_美脚丝袜一区二区三区在线观看_欧美91视频
亚洲成人免费电影| 午夜在线成人av| 国产一区美女| 国产视频一区在线观看| 人人精品人人爱| 欧美三级资源在线| 久久精品99久久久| 精品蜜桃在线看| 成人一区二区三区中文字幕| 日韩欧美激情四射| av亚洲产国偷v产偷v自拍| 欧美日韩综合不卡| 国产一区二区三区精品欧美日韩一区二区三区 | 制服丝袜亚洲网站| a4yy欧美一区二区三区| 日韩欧美一区二区视频| av在线不卡免费看| 亚洲欧美日本国产专区一区| 2020国产精品自拍| 99re热这里只有精品免费视频| 欧美经典一区二区| 精久久久久久| 日韩av在线播放中文字幕| 91福利小视频| 91啪九色porn原创视频在线观看| 久久久99久久| 性8sex亚洲区入口| 美女在线一区二区| 欧美日韩大陆在线| 91亚洲精品一区二区乱码| 精品成人免费观看| 国产嫩草一区二区三区在线观看| 天天色图综合网| 精品国产成人系列| 国产一区二区在线观看免费播放| 日韩高清一区二区| 91精品蜜臀在线一区尤物| 国内精品久久久久久久果冻传媒| 亚洲精品视频在线看| 欧美日本视频在线| 欧美高清一区| 亚洲欧洲一区二区三区| 777午夜精品免费视频| aaa国产一区| 日韩有码一区二区三区| 在线成人av影院| 99这里有精品| 激情综合色播激情啊| 中文字幕一区三区| 欧洲在线/亚洲| 国产在线视频欧美一区二区三区| 午夜视频一区在线观看| 欧美色精品在线视频| 91色porny| 性做久久久久久| 国产欧美综合在线观看第十页| 国产精品久久久对白| 成人黄色在线看| 亚洲免费av在线| 久久综合色鬼综合色| 一区二区欧美日韩| 女人天堂亚洲aⅴ在线观看| 欧美国产日韩a欧美在线观看| 在线观看av一区二区| 欧美永久精品| 国产成人在线免费| 一区二区三区欧美在线观看| 精品伦理精品一区| 久久久久久夜| 最新亚洲视频| 丁香六月综合激情| 久久精品国产免费| 亚洲欧美综合色| 337p粉嫩大胆色噜噜噜噜亚洲| 国产伦精品一区二区三| 97se亚洲国产综合自在线| 日韩avvvv在线播放| 欧美mv和日韩mv的网站| 在线免费观看日本欧美| 在线国产精品一区| 99国产精品久久久久久久久久 | 99视频+国产日韩欧美| 午夜精品福利在线| 欧美激情一区二区| 欧美一级艳片视频免费观看| 老鸭窝亚洲一区二区三区| 欧美日韩99| 不卡av在线网| 国内外精品视频| 中文字幕亚洲区| 国内精品久久国产| 99精品视频免费在线观看| 久久精品国产亚洲一区二区三区| 亚洲成人av电影| 久久久99免费| ww亚洲ww在线观看国产| 麻豆91精品| 性一交一乱一区二区洋洋av| 女人色偷偷aa久久天堂| 高清成人免费视频| 性感美女久久精品| 国产精品久久久久久一区二区三区| 日韩视频一区二区在线观看| 在线观看日韩精品| 久久婷婷麻豆| 久久久久久久高潮| av成人毛片| 国产精品资源| 国产精品久久久对白| 亚洲综合社区| 国产一级久久| 久久久久综合一区二区三区| 99视频一区| 久久精品国产99精品国产亚洲性色| 伊人久久综合| 国产精品嫩草99av在线| 不卡一卡二卡三乱码免费网站| 从欧美一区二区三区| 国产99久久久国产精品免费看| 国产mv日韩mv欧美| 国产精品456露脸| 国产一区二区三区在线看麻豆| 免费视频一区二区| 久久精品久久久精品美女| 精品影视av免费| 久久精品国产一区二区三区免费看| 久久99深爱久久99精品| 久久99久久99小草精品免视看| 国精产品一区一区三区mba视频 | 国产伦精品一区二区三区免费迷 | 亚洲免费在线观看视频| 国产精品福利一区| 一区二区高清在线| 亚洲一区二区三区在线看| 奇米亚洲午夜久久精品| 日韩电影在线免费看| 国产麻豆精品在线| 成年人国产精品| 亚洲黑丝一区二区| 国产精品一区毛片| 欧美老肥妇做.爰bbww| 日韩一二三四区| 国产精品另类一区| 亚洲欧美一区二区三区极速播放| 亚洲成a人v欧美综合天堂下载| 一区二区三区日韩精品视频| 蜜桃一区二区三区在线| 蜜乳av一区二区| www.亚洲色图| 91视频免费看| 亚洲黄色成人| 欧美日韩在线播放一区| 日韩视频在线观看一区二区| 国产亚洲一区二区三区| 国产精品久久久久永久免费观看| 婷婷开心激情综合| 国产一区二区三区四区五区美女| 国产传媒欧美日韩成人| 欧美精品系列| 色素色在线综合| 欧美一卡二卡三卡| 亚洲色图欧美偷拍| 亚洲电影第三页| 国产高清成人在线| 好吊一区二区三区| 亚洲欧美99| 91精品国产欧美日韩| 亚洲乱码国产乱码精品精的特点| 日本不卡中文字幕| 欧美日韩三级电影在线| 亚洲综合社区| 久久久国产精品麻豆| 一区二区欧美国产| 91视视频在线直接观看在线看网页在线看 | 色综合天天综合网天天看片| 久久亚洲国产精品一区二区| 欧美一二三四区在线| 一区二区三区中文在线观看| 久久精品国产第一区二区三区| 欧美三级视频| 在线日韩国产精品| 国产精品久久二区二区| 蜜臀av一区二区在线免费观看 | 欧美精品丝袜中出| 一区二区三区免费在线观看| 久久69国产一区二区蜜臀| 在线欧美日韩| 欧美美女网站色| 亚洲图片欧美综合| 成人免费视频免费观看| 久久一区中文字幕| 久久嫩草精品久久久精品一| 日韩成人一级片| 成人黄色软件下载| 91成人网在线| 欧美激情一区不卡| 国产一区二区精品在线观看| 亚洲一区二区三区涩| 欧美精品一区二区蜜臀亚洲| 蜜臀av亚洲一区中文字幕|