_AddRef和_Release的问题!(100分)

  • 主题发起人 主题发起人 mywyn
  • 开始时间 开始时间
M

mywyn

Unregistered / Unconfirmed
GUEST, unregistred user!
大家知道,如果你对接口进行了引用,编译器会在适当的地方插入 _AddRef。如果引用指针被释放或
置为nil编译器会调用_Release。现在我就要把这该死的功能去掉。

举个例子,类A实现了接口B,类C对B进行了引用,并把引用指针定义在了private中。
这样,我就必须保证类C实例必须在类A实例释放前释放。否则就要出错。

在一个复杂的充斥各种接口的系统中要时刻记住这样的规则是一件累人的事情。
望各位富翁不吝赐教。
 
自己实现B 的IUnknown 接口
 
不用接口,用继承。纯虚函数。
 
to jsxjd:
没用的,所有的接口都必须实现QueryInterface,_AddRef和_Release。给人的感觉好像
是专为COM设计,让人很不爽。

to Yukin:
我主要是用来实现类似于多重继承的功能,所以一定要用接口。

 
论坛的“订阅邮件通知”怎么没有效?
 
在适当的地方你也可以调用_addref,是有写麻烦,没办法,DELPHI里没有智能指针
 
如果使用接口,就应该完全让其为你管理对象的生命周期,
而不应该参杂手工管理在其中,否则把自己搞糊涂是在所难免的。

你的类A的对象,既然以接口形式将自己的生命周期管理权交出,
就不应该再显式释放,应该统一使用接口来自动管理

因为你将对象作为接口传递出去同时就是将控制权交给引用计数管理机制,
而对象的生命周期控制权不应该有两个或更多人控制
即一旦用接口使用一个对象,就全部使用接口来访问、控制此对象
 
大佬,如果C不释放,B接口是不会被释放的。
 
to wlmmlw:
对啊,从C获得A类实例的B接口开始,A类的这个实例的生命期所有权就应该交由其
引用计数机制控制,A对象本身和C都没有权利释放这个对象,只能建议,最终决定权
在引用计数本身,这样就可以保证只要有人用了A对象的B接口或其它共用IUnknown的接口,
A的这个对象就存在,也就解决了提问者的困惑啊
 
>>如果使用接口,就应该完全让其为你管理对象的生命周期,
而不应该参杂手工管理在其中,否则把自己搞糊涂是在所难免的。

这句话我不敢苟同,考虑这么一种情况:
由于类A的功能比较特殊,初始化耗时较长,所以我把类A设计成一个无状态的单例模式。
如果我把生命周期完全交给引用计数的话,就会引起类A频繁的释放和创建,因为有多个类会根据
运行状态来决定是否使用类A。而这是不允许的。

其实从D6开始,Delphi就已经考虑了这一情况,并试图把接口从纯粹的为COM服务变成OP语言的一部分
举两个例子:
D6
TComponent = class(TPersistent, IInterface, IInterfaceComponentReference)
D5
TComponent = class(TPersistent)
在D6中,如果你的类是从TComponent及其继承类继承并要实现某一接口可以不实现那三个该死的函数,
因为TComponent已经有了缺省实现。当然在D5中也可以从TInterfacedObject等有限的几个类
继承,不过范围太小。

d6
IInterface = interface
['{00000000-0000-0000-C000-000000000046}']
function QueryInterface(const IID: TGUID
out Obj): HResult
stdcall;
function _AddRef: Integer
stdcall;
function _Release: Integer
stdcall;
end;

IUnknown = IInterface;

TInterfacedObject = class(TObject, IInterface)


d5
IUnknown = interface
['{00000000-0000-0000-C000-000000000046}']
function QueryInterface(const IID: TGUID
out Obj): HResult
stdcall;
function _AddRef: Integer
stdcall;
function _Release: Integer
stdcall;
end;

TInterfacedObject = class(TObject, IUnknown)

D6中所有的接口是从IInterface继承而不是IUnknown,而且一些跟COM无关的类也是从IInterface继承。
虽然目前本质上是一样的,不过难保将来不发生变化。

以上两点,变化虽然不大,不过却显示出Borland希望Delphi能够更好的为面向接口编程服务。


 
听说以后BORLAND把深根继承该成接口继承,不知道是不是真的?
 
//必须保证类C实例必须在类A实例释放前释放。否则就要出错。
本来就应该这样啊。要是 A 都释放了,如何实现 B ?
当然,你的意思可能是在 A 释放过后就不在 C 中引用该接口,
那你可以先将 C 中接口指针置空,然后在释放 A,如:
var
X: Y;
begin
X := YourIntf;
YourIntf := nil;
X.Free;
end;
 
//如果引用指针被释放或置为nil编译器会调用_Release
这么自动你还要管呀!
 
to beta:
你的意思我明白,可能是我没说清楚。
接口引用的生命周期是跟C一样的,也就是说只能在C的Destroy中把接口指针置空。
A和C的释放都是在程序退出时进行,如果C在A后释放,无论你是否把接口指针置空都会
出错。
 
to mywyn:
如果你的对象构造比较耗时,建议用内存池将之缓冲起来,可以自己实现IUnkonwn接口,
在引用计数为0时将对象放回缓冲池,避免频繁释放,如果是无状态的话,应该很好实现
Delphi中的接口,从D3为支持COM而加入,到现在可以独立使用,虽然有一些变化,
但本质上还是基于COM那套运行机制的,并不等同于Java之类语言中的接口。
因此在COM中使用接口就应该遵循COM的生命期管理,否则直接用指针好了。
即使用接口,也可以使用代理模式,用一个代理类管理你的耗时对象,这样可以解决
你关注的矛盾问题,MS.NET中的WeakRefrence就是基于这个思想的
 
to flier:
我用接口主要就是解决多重继承的问题,不过现在看来是得不偿失。我已经开始把
一些生命周期不定的接口改成纯虚类加聚合。生命周期固定的我只需在最终释放的
时候注意一下顺序就行了。

不知什么时候Delphi的接口才能像Java一样啊!!!
 
to mywyn:
你可以用设计模式中的类工厂来管理你的类,、
良好的类工厂类可以帮助你管理好你的类,
同时通过类组合来实现多重继承的功能,
 
to flier:
不知vs.net有没有提供基类库源码,就像Delphi的vcl。我装了一个,怎么也找不到。
flier兄能说明一下吗?
 
MS的发行版本是不附带源代码的,不过MS发布了一个CLI(CLR的子集)的实现源码,
而且有一个开源的GNU项目mono也发布了源码。虽然这两个项目中关于BCL的源代码都不全,
但基本上够用,也可以找一个反编译工具直接看BCL带的DLL,可以直接反编译成C#代码,
除了没有局部变量名和注释,其它的都可以直接看到……
 
非常感谢flier兄的回答。
 
后退
顶部