转至繁体中文版     | 网站首页 | 图文教程 | 资源下载 | 站长博客 | 图片素材 | 武汉seo | 武汉网站优化 | 
最新公告:     敏韬网|教学资源学习资料永久免费分享站!  [mintao  2008年9月2日]        
您现在的位置: 学习笔记 >> 图文教程 >> 软件开发 >> Delphi程序 >> 正文
Delphi的接口陷阱         ★★★★

Delphi的接口陷阱

作者:闵涛 文章来源:闵涛的学习笔记 点击数:1135 更新时间:2009/4/23 18:30:57

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;

[1] [2]  下一页


没有相关教程
教程录入:mintao    责任编辑:mintao 
  • 上一篇教程:

  • 下一篇教程:
  • 【字体: 】【发表评论】【加入收藏】【告诉好友】【打印此文】【关闭窗口
      注:本站部分文章源于互联网,版权归原作者所有!如有侵权,请原作者与本站联系,本站将立即删除! 本站文章除特别注明外均可转载,但需注明出处! [MinTao学以致用网]
      网友评论:(只显示最新10条。评论内容只代表网友观点,与本站立场无关!)

    同类栏目
    · C语言系列  · VB.NET程序
    · JAVA开发  · Delphi程序
    · 脚本语言
    更多内容
    热门推荐 更多内容
  • 没有教程
  • 赞助链接
    更多内容
    闵涛博文 更多关于武汉SEO的内容
    500 - 内部服务器错误。

    500 - 内部服务器错误。

    您查找的资源存在问题,因而无法显示。

    | 设为首页 |加入收藏 | 联系站长 | 友情链接 | 版权申明 | 广告服务
    MinTao学以致用网

    Copyright @ 2007-2012 敏韬网(敏而好学,文韬武略--MinTao.Net)(学习笔记) Inc All Rights Reserved.
    闵涛 投放广告、内容合作请Q我! E_mail:admin@mintao.net(欢迎提供学习资源)

    站长:MinTao ICP备案号:鄂ICP备11006601号-18

    闵涛站盟:医药大全-武穴网A打造BCD……
    咸宁网络警察报警平台