線上訂房服務-台灣趴趴狗聯合訂房中心
發文 回覆 瀏覽次數:2556
推到 Plurk!
推到 Facebook!

為何使用Interface會出現Access violation?

 
pedro
尊榮會員


發表:152
回覆:1187
積分:892
註冊:2002-06-12

發送簡訊給我
#1 引用回覆 回覆 發表時間:2006-09-15 16:31:36 IP:60.248.xxx.xxx 未訂閱

請問我使用下面的程式碼,為何出現記憶體錯誤,是我那邊觀念不對麼? 請大大們指教,謝謝

type
ICanJump=interface
['{102D9C49-6245-4171-986C-263BF2811CCA}']
function Jump:string;
end;


TBird=class(TInterfacedObject,ICanJump)
function Jump:string;
end;

function TBird.Jump: string;
begin
Result:='Bird Jump';
end;

procedure TForm5.Button1Click(Sender: TObject);
var
CanJump:ICanJump;
source:TInterfacedObject;
begin
source:=TBird.Create;
if supports(source,ICanJump) then
begin
CanJump:=source as ICanJump;
ShowMessage(CanJump.Jump);
end;
end;

aftcast
站務副站長


發表:81
回覆:1485
積分:1763
註冊:2002-11-21

發送簡訊給我
#2 引用回覆 回覆 發表時間:2006-09-17 07:46:36 IP:61.229.xxx.xxx 未訂閱

post半天沒人回,我就閒閒插花一下好了。
不過先聲明我很久不用delphi,也不熟了。

覺得應該是
CanJump:ICanJump;
CanJump.Jump

有問題,interface就許多語言來說是不可能有實體的,也不能直接叫用。若非interface的父類別,那就可以直接叫用,也可以create。而你直接CanJump.Jump,應該就會出現有名的AV (Access Violation ),不是AV女優喔!

===================引 用 文 章===================

請問我使用下面的程式碼,為何出現記憶體錯誤,是我那邊觀念不對麼? 請大大們指教,謝謝

------


蕭沖
--All ideas are worthless unless implemented--

C++ Builder Delphi Taiwan G+ 社群
http://bit.ly/cbtaiwan
pcplayer99
尊榮會員


發表:146
回覆:790
積分:632
註冊:2003-01-21

發送簡訊給我
#3 引用回覆 回覆 發表時間:2006-09-17 10:07:25 IP:202.105.xxx.xxx 未訂閱

===================引 用 文 章===================

請問我使用下面的程式碼,為何出現記憶體錯誤,是我那邊觀念不對麼? 請大大們指教,謝謝

type
ICanJump=interface
['{102D9C49-6245-4171-986C-263BF2811CCA}']
function Jump:string;
end;


TBird=class(TInterfacedObject,ICanJump)
function Jump:string;
end;

function TBird.Jump: string;
begin
Result:='Bird Jump';
end;

procedure TForm5.Button1Click(Sender: TObject);
var
CanJump:ICanJump;
source:TInterfacedObject;
begin
source:=TBird.Create;
if supports(source,ICanJump) then
begin
CanJump:=source as ICanJump;
ShowMessage(CanJump.Jump);
end;
end;


=======================================


你好。把你的城市改一下就没问题了:


procedure TForm1.Button1Click(Sender: TObject);
var
CanJump:ICanJump;
source: TInterfacedObject;
S: string;
IID: TGUID;
begin
source:=TBird.Create;
if supports(source,ICanJump,CanJump) then
begin
S := CanJump.Jump;
CanJump := nil;
Label1.Caption := S;
end;
end;

------------------------------------------

你的程序和我的可以正常执行的程序的区别是:

1. 我的: if supports(source,ICanJump,CanJump) then
2. 你的: if supports(source,ICanJump) then

你可以看看 Delphi 的 Help 关于 supports:

function Supports(const Instance: IInterface ; const IID: TGUID; out Intf): Boolean; overload;
function Supports(const Instance: TObject; const IID: TGUID; out Intf): Boolean; overload;
function Supports(const AClass: TClass; const IID: TGUID): Boolean; overload;

注意 help 里的这一句:
If Instance supports the interface, Supports returns the interface as the Intf parameter and returns true. If AClass supports the interface, Supports does not return an interface, but still returns true.

你的程序实际上是采用了:function Supports(const AClass: TClass; const IID: TGUID): Boolean; overload; 而这个是检查一个 Class 是否支持某个 Interface,并不会返回一个 Interface 的实例,亦即其实例是 nil,调用它,当然 AV。

我使用了 function Supports(const Instance: TObject; const IID: TGUID; out Intf): Boolean; overload; 这一句,是检查某个对象(也就是某个类的实例)是否支持某个 Interface,如果支持,返回的第三个参数就是它实现的 Interface 的实例。因此可以正确调用那个 Interface。

其实,很多时候,只需要去看看 Help 就很清楚了。
aftcast
站務副站長


發表:81
回覆:1485
積分:1763
註冊:2002-11-21

發送簡訊給我
#4 引用回覆 回覆 發表時間:2006-09-17 13:39:45 IP:61.229.xxx.xxx 未訂閱

原來delphi中使用interface的方法和c 有一樣,也有不一樣。

一樣的地方是無法直接把instance直接upcast(向上轉型)就可以使用interface。因此原出題人使用source as ICanJump;這句話會失效!

不一樣的地方是c 要使用Instance->ICanJump::Jump( )來呼叫
而delphi要靠Supports( ),來幫忙傳指向ICanJump的reference,即相當(Instance->ICanJump)的位址。

學習了!


===================引 用 文 章===================

source:=TBird.Create;
if supports(source,ICanJump) then
begin
CanJump:=source as ICanJump; //無法直接用instance upcast 使用interface

source:=TBird.Create;
if supports(source,ICanJump,CanJump) then //透過supports的方法回傳ICanJump所在的位址



------


蕭沖
--All ideas are worthless unless implemented--

C++ Builder Delphi Taiwan G+ 社群
http://bit.ly/cbtaiwan
pedro
尊榮會員


發表:152
回覆:1187
積分:892
註冊:2002-06-12

發送簡訊給我
#5 引用回覆 回覆 發表時間:2006-09-17 21:22:01 IP:61.216.xxx.xxx 未訂閱

謝謝aftcast及pcplayer99大大的回應

特別是pcplayer99告知小弟supports還有另一個重載式可供取得interface,
另外釋放不以物件free來釋放,而是assign nil即可.

經修改後如下,可以執行無誤了,謝謝您們

type
ICanJump=interface
['{7E941BAB-3143-4C11-B0FB-177A5EDEE32E}']
function Jump:string;
end;

TBird=class(TInterfacedObject,ICanJump)
function Jump:string;virtual;abstract;
end;

TBird1=class(TBird)
function Jump:string;override;
end;

TBird2=class(TBird)
function Jump:string;override;
end;

function TBird1.Jump: string;
begin
Result:='Bird1 jump';
end;

function TBird2.Jump: string;
begin
Result:='Bird2 Jump';
end;

procedure TForm3.Button1Click(Sender: TObject);
var
CanJump:ICanJump;
source:TBird;
begin
source:=TBird1.Create;
if supports(source,ICanJump,CanJump) then
ShowMessage(CanJump.Jump);
CanJump:=nil;
source:=TBird2.Create;
if supports(source,ICanJump,CanJump) then
ShowMessage(CanJump.Jump);
CanJump:=nil;
end;

pedro
尊榮會員


發表:152
回覆:1187
積分:892
註冊:2002-06-12

發送簡訊給我
#6 引用回覆 回覆 發表時間:2006-09-18 11:39:57 IP:60.248.xxx.xxx 未訂閱

延續這個問題,再做變化,請教兩位大大,

下面的程式碼為何DoIt2有時會執行到有時不會,執行到還會AV錯誤?

依小弟的見解,IOne(Intf).DoIt執行完畢時,介面Intf會被釋放,而物件person並末釋放,我再次去取得ITwo介面,理應可取得及完整執行才對,這邊實在不解?

type
IOne=interface
['{96E11EBF-B079-41D4-A028-EB2EC64FA77F}']
procedure DoIt;
end;

ITwo=Interface
['{F1096DE6-72E1-46A3-A584-93A1734892EA}']
procedure DoIt;
end;

TPerson=class(TInterfacedObject,IOne,ITwo)
procedure IOne.DoIt=DoIt1;
procedure ITwo.DoIt=DoIt2;
procedure DoIt1;
procedure DoIt2;
procedure DoNomal;
end;

procedure TPerson.DoIt1;
begin
ShowMessage('DoIt1');
end;

procedure TPerson.DoIt2;
begin
ShowMessage('DoIt2');
end;

procedure TPerson.DoNomal;
begin
ShowMessage('Normal');
end;

procedure TForm1.Button1Click(Sender: TObject);
var
person:TPerson;
Intf:IInterface;
begin
person:=TPerson.Create;
if supports(person,IOne,intf) then
IOne(Intf).DoIt;
person.DoNomal;
if supports(person,ITwo,intf) then
ITwo(Intf).DoIt;
end;

pcplayer99
尊榮會員


發表:146
回覆:790
積分:632
註冊:2003-01-21

發送簡訊給我
#7 引用回覆 回覆 發表時間:2006-09-22 23:58:48 IP:219.134.xxx.xxx 未訂閱
procedure TForm1.Button1Click(Sender: TObject);
var
person:TPerson;
Intf:IInterface;
begin
person:=TPerson.Create;
if supports(person,IOne,intf) then
IOne(Intf).DoIt; 《--- 执行到这里, Intf 还在。如果这时候,这个方法结束,比如:
end; <--- 方法立即结束。因为离开了这个方法,这个方法的局部的变数 intf 自动被清除,就会导致 Person 的 interface 的引用计数为 0 而导致 Person 被释放。

但你的程式并没有立即结束。这时候,如果写成:

procedure TForm1.Button1Click(Sender: TObject);
var
person:TPerson;
Intf:IInterface;
begin
person:=TPerson.Create;
if supports(person,IOne,intf) then
IOne(Intf).DoIt;
Intf := nil;
Person.DoSometing.....; <--- 因为前面释放了它的 interface ,会导致 Person 被自动 Free,结果,这句肯定会出 AV 错误。
end;

所以,你的程式:

procedure TForm1.Button1Click(Sender: TObject);
var
person:TPerson;
Intf:IInterface;
begin
person:=TPerson.Create;
if supports(person,IOne,intf) then
IOne(Intf).DoIt;
person.DoNomal;
if supports(person,ITwo,intf) then <---- 这里,等于放弃了刚才拿到的 IOne,也就是很可能要让 Person 被 Free,但又立即拿到 ITwo,如果拿到 ITwo 先于 IOne 释放,则 Person 不会被 Free. 所以,这里执行的结果很难说。
ITwo(Intf).DoIt;
end;

对于上述代码,实际写程式的时候,千万别这样写。因为这样很不确定。

===================引 用 文 章===================

延續這個問題,再做變化,請教兩位大大,

下面的程式碼為何DoIt2有時會執行到有時不會,執行到還會AV錯誤?

依小弟的見解,IOne(Intf).DoIt執行完畢時,介面Intf會被釋放,而物件person並末釋放,我再次去取得ITwo介面,理應可取得及完整執行才對,這邊實在不解?

type
IOne=interface
['{96E11EBF-B079-41D4-A028-EB2EC64FA77F}']
procedure DoIt;
end;

ITwo=Interface
['{F1096DE6-72E1-46A3-A584-93A1734892EA}']
procedure DoIt;
end;

TPerson=class(TInterfacedObject,IOne,ITwo)
procedure IOne.DoIt=DoIt1;
procedure ITwo.DoIt=DoIt2;
procedure DoIt1;
procedure DoIt2;
procedure DoNomal;
end;

procedure TPerson.DoIt1;
begin
ShowMessage('DoIt1');
end;

procedure TPerson.DoIt2;
begin
ShowMessage('DoIt2');
end;

procedure TPerson.DoNomal;
begin
ShowMessage('Normal');
end;

procedure TForm1.Button1Click(Sender: TObject);
var
person:TPerson;
Intf:IInterface;
begin
person:=TPerson.Create;
if supports(person,IOne,intf) then
IOne(Intf).DoIt;
person.DoNomal;
if supports(person,ITwo,intf) then
ITwo(Intf).DoIt;
end;

系統時間:2024-03-29 20:21:42
聯絡我們 | Delphi K.Top討論版
本站聲明
1. 本論壇為無營利行為之開放平台,所有文章都是由網友自行張貼,如牽涉到法律糾紛一切與本站無關。
2. 假如網友發表之內容涉及侵權,而損及您的利益,請立即通知版主刪除。
3. 請勿批評中華民國元首及政府或批評各政黨,是藍是綠本站無權干涉,但這裡不是政治性論壇!