我不应该将接口作为const传递吗? [英] Should I not pass an interface as const?

查看:82
本文介绍了我不应该将接口作为const传递吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我最近(再次)遇到将接口作为 const 会泄漏引用。



如果声明您的方法将接口变量传递为 const ,例如:

 过程Frob(const Grob:IGrobber); 

解决方法是简单地删除 const

 程序Frob(Grob:IGrobber); 

我知道 const (和 var out )允许您通过引用传递项目。对于结构,这将保存参数副本;



对于 Object / 指针 / 接口无需通过引用传递,因为它是引用



为了不再遇到这个问题,我进行了一次十字军东征。我在所有源代码树中搜索了以下内容:

  const [A-Za-z] + \:I [AZ] 

然后我删除了大约150个实例,其中我以 const 的形式传递了接口。



但是有些我无法改变。 TWebBrowser 回调事件声明为:

  OnDocumentComplete(Sender:TObject ; const pDisp:IDispatch; var URL:OleVariant); 
\ ___ /
|

我走得太远了吗?我做了一件不好的事吗?



编辑:或者,用一个较少的基于观点 样式问题:是否有将接口作为const传递的严重缺点?



奖金:当Delphi不(总是)增加接口引用计数时,它们违反了 COM规则


引用计数规则



规则1: AddRef 必须为接口指针的每个新副本调用,而 Release 则要求对接口指针的每次销毁接口指针,除非后续规则明确允许。



规则2 :一段关系代码中的特殊知识t生命周期的开始和结束接口指针的两个或多个副本可以允许省略 AddRef / Release 对。


解决方案

< blockquote>

如果声明您的方法将接口变量作为const传递,则会发生这种情况,例如:

  procedure Frob(const Grob:IGrobber); 


那不是很正确。为了存在泄漏,您需要在代码中没有引用新创建对象的内容。因此,如果您这样写:

  Frob(grob); 

没有问题,因为接口 grob 已经至少具有


问题是在您编写时出现的:

  Frob(TGrobberImplementer.Create); 

在这种情况下,没有任何东西引用该接口,因此它被泄漏了。好吧,只要 Frob 的实现中没有引用它,它就会被泄漏。


我做了一件坏事吗?


嗯,这取决于。我认为您所做的事情不会带来特别糟糕的事情。在性能方面存在一个缺点,因为您所有接受接口参数的函数现在都必须使用隐式try / finally块来添加和释放引用。只有您才能判断这是否重要。


更重要的问题与您无法控制的代码有关。您给

 程序OnDocumentComplete(Sender:TObject; const pDisp:IDispatch; var URL:OleVariant);以

为例。那里没有问题,因为您永远不会调用该方法。这是您实施的事件处理程序。框架将其调用,并传递已被引用的接口。


真正的问题来自RTL中声明的方法或您调用的任何其他第三方代码。如果调用方法,并且它们使用 const 接口参数,则可能会陷入陷阱。


很容易

  grob:= TGrobberImplementer.Create; 
Frob(grob);

我处理该问题的理由如下:


  1. 按值传递接口参数会降低性能。

  2. 我不能确保我调用的每个方法都按值接受接口参数。

  3. 因此,我接受这样一个事实,即我至少需要在某些时候处理调用 const 接口参数的事实。

  4. 由于某些时候我不得不处理它,并且由于我讨厌不一致,所以我选择一直拥抱它。

  5. 因此,我选择创建所有接口我编写的方法中的参数是 const

  6. 因此,我确保除非将接口作为参数,否则我绝不将其作为参数传递。已被变量引用。


I recently came across (again) the Delphi compiler code-gen bug when passing an interface as const leaks a reference.

This happens if your method is declared to pass an interface variable as const, e.g.:

procedure Frob(const Grob: IGrobber);

and the fix is to simply remove the const:

procedure Frob(Grob: IGrobber);

I understand that const (and var, and out) allow you to pass items by reference. In the case of a structure this saves an argument copy; letting you instead simply pass the pointer to the item.

In the case of an Object/Pointer/Interface there is no need to pass by reference, since it is a reference; it already will fit in a register.

In order to never have this problem again, i went on a crusade. I searched all my source trees for:

const [A-Za-z]+\: I[A-Z]

And i removed about 150 instances where i pass an interface as const.

But there are ones that i cannot change. The TWebBrowser callback events are declared as:

OnDocumentComplete(Sender: TObject; const pDisp: IDispatch; var URL: OleVariant);
                                    \___/
                                      |
                                      ?

Have i gone too far? Have i done a bad thing?

Edit: Or, to phrase it in a less "based on opinion" style question: are there any serious down-sides to not passing an interface as const?

Bonus: When Delphi does not (always) increment a interface reference count they are violating The Rules of COM:

Reference-Counting Rules

Rule 1: AddRef must be called for every new copy of an interface pointer, and Release called for every destruction of an interface pointer, except where subsequent rules explicitly permit otherwise.

Rule 2: Special knowledge on the part of a piece of code of the relationships of the beginnings and the endings of the lifetimes of two or more copies of an interface pointer can allow AddRef/Release pairs to be omitted.

So while it may be an optimization that the compiler can take advantage of, it has to do it correctly so as to not violate the rules.

解决方案

This happens if your method is declared to pass an interface variable as const, e.g.:

procedure Frob(const Grob: IGrobber);

That's not quite right. In order for there to be a leak, you need for there to be nothing in the code that ever takes a reference to the newly created object. So if you write:

Frob(grob);

There there's no problem because the interface grob already has at least one reference.

The problem arises when you write:

Frob(TGrobberImplementer.Create);

In that scenario nothing takes a reference to the interface and so it is leaked. Well, it will be leaked so long as nothing in the implementation of Frob takes a reference to it.

Have I done a bad thing?

Well, that depends. I don't think anything particularly bad will come of what you have done. There is a downside in terms of performance because all of your functions that accept interface parameters will now have to add and release references, using implicit try/finally blocks. Only you can judge whether that matters.

The more significant issue relates to code that is outside of your control. You give

procedure OnDocumentComplete(Sender: TObject; const pDisp: IDispatch; var URL: OleVariant);

as an example. There's no issue there because you never call that method. It's an event handler that you implement. The framework calls it, and it passes an interface that is already referenced.

The real problem comes from methods declared in the RTL or any other third party code that you call. If you are calling the methods, and if they use const interface arguments then you can fall into the trap.

It's easy enough to work around, albeit tiresome.

grob := TGrobberImplementer.Create;
Frob(grob);

My reasoning to deal with the issue goes like this:

  1. There is a performance cost in passing interface parameters by value.
  2. I cannot ensure that every method that I call will accept interface parameters by value.
  3. Therefore, I embrace the fact that I need to deal with calling const interface parameters at least some of the time.
  4. Since I have to deal with it some of the time, and since I hate inconsistency, I choose to embrace dealing with it all of the time.
  5. Therefore, I choose to make all interface parameters in methods that I write be const.
  6. And consequently I make sure that I never pass an interface as an argument unless it is already referenced by a variable.

这篇关于我不应该将接口作为const传递吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆