我应该在每个 Object.Create 之后放置一个 try-finally 块吗?

发布于 2024-09-02 21:08:33 字数 343 浏览 2 评论 0原文

我有一个关于 OO Delphi 最佳实践的一般性问题。目前,我在创建对象的任何地方放置 try-finally 块,以便在使用后释放该对象(以避免内存泄漏)。例如:

aObject := TObject.Create;
try
  aOBject.AProcedure();
  ...
finally
  aObject.Free;
end;

而不是:

aObject := TObject.Create;
aObject.AProcedure();
..
aObject.Free;

您认为这是好的做法,还是开销太大?那么性能呢?

I have a general question about best practice in OO Delphi. Currently, I put try-finally blocks anywhere I create an object to free that object after usage (to avoid memory leaks). E.g.:

aObject := TObject.Create;
try
  aOBject.AProcedure();
  ...
finally
  aObject.Free;
end;

instead of:

aObject := TObject.Create;
aObject.AProcedure();
..
aObject.Free;

Do you think it is good practice, or too much overhead? And what about the performance?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

咽泪装欢 2024-09-09 21:08:33

使用 try-finally 绝对是最佳实践。

如果引发异常,该对象将被释放。

至于性能:在优化之前测量

It's definitely best practice to use try-finally.

In the event of an exception being raised, that object will be freed.

As for performance: measure before you optimise.

征棹 2024-09-09 21:08:33

在我看来,对象构造不应后面只有一个原因 (或者像 Mason 指出的那样)一个 try / finally堵塞。

  1. 如果一个对象的生命周期由另一个对象管理。

这种管理可以采取三种形式:

  1. 对象的引用具有超出本地块的范围,并在其他地方释放 - 就像在析构函数中释放的字段成员一样。
  2. 对象立即添加到列表中,列表负责稍后释放该对象。
  3. 具有关联生命周期管理器的对象,就像在构造时将所有者传递给 VCL 控件的方式一样。

对于#1当引用具有更广泛的范围时,如果没有立即构造引用,则应立即将其设置为 nil。这样,当检查参考值时,您就知道读数准确。这对于作为较大类的一部分构造的成员对象最常见,然后在父对象被销毁时被清除。

对于#2当将对象添加到列表中时,您需要使用try- except块(我为数不多的几次之一)使用一个)以防万一在构造对象之后和将其添加到管理列表之前发生异常。理想情况下,构造后的第一行是将其添加到列表中,或者列表实际上是一个工厂类,它为您提供已添加到列表中的对象。

对于#3当一个对象有另一个生命周期管理器时,您确实应该确保由该管理器管理它是正确的做法。如果您正在构建 VCL 控件,您可能会想让表单(或任何其他控件)拥有它,但这实际上会给构建和销毁带来额外的开销。如果可能的话,您应该显式释放它,如果您将控件打开一次,那么尤其如此,那么您知道您将在表单的析构函数中或在它关闭时释放它。唯一不能执行此操作的情况是控件创建更加动态。

所以,是的,使用大量 try /finally 块是最佳实践。您应该只有几个 try / except 块,并且大多数块都应该捕获非常特定的异常类型,和/或重新引发异常。如果您的 try / except 多于 try / finally 块,那么您做错了

In my opinion, there is only one reason an objects construction should not be followed by (or "in" as Mason pointed out) a try / finally block.

  1. If an objects' lifetime is being managed by another object.

This management can take three forms:

  1. An object's reference has a scope beyond the local block and is freed elsewhere - like a field member freed in the destructor.
  2. An object immediately added to a list which is in charge of freeing the object later.
  3. An object with an associated lifetime manager, like how you pass the owner to a VCL control on construction.

With #1, when the reference has a broader scope, the reference should be set to nil immediately if it isn't constructed right away. That way when it is checked for reference you know you have an accurate reading. This is most common with member objects that are constructed as part of a larger class, and then cleaned up when the parent object is destroyed.

With #2, when an object is added to a list, you want to use a try-except block (one of the few times I use one) just in case an exception occurs after the object is constructed and before it is added to the managing list. Ideally the first line after the construction is adding it to the list, or the list is actually a factory class that gives you an object already added to the list.

With #3, when an object has another lifetime manager, you really should make sure having it managed by that manager is the right thing to do. If you are constructing a VCL control, you may be tempted to have the form (or any other control) own it, but that actually puts additional overhead on the construction and destruction. If possible you should explicitly free it, this is especially true if you put the control on once, then you know you will be freeing it in your form's destructor or when it closes. The only time you can't do this is if the control creation is more dynamic.

So yes, it is a best practice to use a lot of try / finally blocks. You should only have a few try / except blocks, and most all of them should trap very specific exception types, and / or re-raise the exception. If you have more try / except than try / finally blocks, then you are doing it wrong.

太阳男子 2024-09-09 21:08:33

正如 Frank 所说,“至于性能:优化之前先进行衡量。”重复强调。

另外,如果您在一个方法中创建一堆对象,则不需要为每个对象使用 try..finally 块。这可能会导致丑陋的缩进混乱。 创建,尝试,创建,尝试,创建,尝试,做某事,最后,免费,最后,免费,最后,免费。 呃!相反,您可以在方法顶部将对象引用设置为nil,然后创建所有对象,执行one try 块,并在finally 部分中释放它们。

这将节省一些开销和性能(尽管您可能永远不会注意到差异),但更重要的是,它将使您的代码更干净、更易于阅读,同时保持相同的安全级别。

As Frank said, "as for performance: measure before you optimize." Repeating it to emphasize.

Also, if you're creating a bunch of objects in a method, you don't need to use a try..finally block for each of them. That can lead to an ugly indentation mess. create, try, create, try, create, try, do something, finally, free, finally, free, finally, free. Ugh! Instead, you can set the object references to nil at the top of the method, then create them all, do one try block, and free them all in the finally section.

That'll save some overhead and performance, (though you'll probably never notice the difference,) but more importantly it'll make your code cleaner and easier to read while maintaining the same level of safety.

独木成林 2024-09-09 21:08:33

回答您问题的第二部分:
try finally 几乎没有任何开销。

事实上,有很多方法都有隐式的 try...finally 块。例如,只有一个函数使用任何接口类型的本地变量并分配一个值。

——杰罗恩

To answer part two of your question:
try finally hardly has any overhead.

In fact there are plenty of methods having an implicit try...finally block. For instance just having a function using a local var of any Interface type and assigning that a value has.

--jeroen

仅此而已 2024-09-09 21:08:33

去年在 Delphi 开发者日上,我看到了 Marco Cantu 的一些私人代码,他每次创建任何东西时都会调用 tryfinally。

有人问他这件事,他说他一直在努力这样做。

但是,对于多线程代码来说,当涉及到进入和退出关键部分时,这尤其是一个好主意,尽管这不是主题,但记住这一点是一件好事。

显然,有时这有点唐突,如果你的工作环境文化不适合你的鲁莽,它可能会让你看起来像一双好鞋。但我认为这是个好主意。这有点像 Delphi 尝试强制执行手动垃圾收集。

Last year at Delphi Developer days I saw some of Marco Cantu's private stash of code and he calls try finally every single time he creates anything.

Somebody asked him about it, and he said he tries to do it all the time.

But it's especially a good idea for multi-threaded code when it comes to entering and exiting critical sections, although that's not on the topic, it's a good thing to remember.

Obviously sometimes it's a bit obtrusive and if it's not in the culture of your work environment to be spot-on in your robsutness it may make you look like a goodie-two-shoes. But I think it's a good idea. It's kind of like Delphi's attempt at enforced manual garbage collection.

夏见 2024-09-09 21:08:33

如果您在类的构造函数中创建对象,并且该对象将由封闭类拥有,则您需要在所属类的析构函数中释放它。

我倾向于使用 FreeAndNil() 而不是调用 Free。

编辑:但正如其他人所说,你肯定总是想释放你所创造的东西。

If you're creating the object in a class's constructor and the object will be owned by the enclosing class, you'll want to free it in the owning class's destructor.

I tend to use FreeAndNil() instead of calling Free.

EDIT: But as others have said, you definitely always want to free what you create.

风流物 2024-09-09 21:08:33

是的,如果创建对象的代码负责释放它,这总是一个好主意(必要的)。如果不是,那么 try/finally 是不合适的,但是无论如何,Free 也不是合适的!

但是,让这个样板代码充斥您的“业务逻辑”可能会很麻烦,您可能需要考虑一种方法,它具有释放对象的相同保证,但更干净(并且具有其他好处),比如我自己的 AutoFree() 实现

使用 AutoFree() 您可以编写代码:

aObject := TObject.Create;
AutoFree(@aObject);

aObject.AProcedure();

或者,由于实现使用对引用的引用(以启用自动 NIL'ing 以及 Free'ing),您甚至可以预先注册您希望的引用进行 AutoFree 以使此类内务处理声明远离您的业务逻辑,并使代码的真正“内容”尽可能干净(当可能需要释放多个对象时,这尤其有用):

AutoFree(@aObject1);
AutoFree(@aObject2);

aObject1 := TObject.Create;
aObject1.AProcedure();

// Later on aObject2 is (or may be) also created
 :

未在我的中显示原始发布是后来对支持在单个 AutoFree() 调用中注册多个引用的机制的补充,但我相信如果您希望能够做到这一点,您可以自己找出支持此操作所需的更改:

AutoFree([@aObject1, @aObject2]);

aObject1 := TObject.Create;
aObject1.AProcedure();

// Later on aObject2 is (or may be) also created
 :

Yes it is always a good idea (essential) if the code that creates the object is responsible for free'ing it. If not, then try/finally isn't appropriate but then again neither is .Free in any case!

However, it can be cumbersome to have this boilerplate code peppering your "business logic", and you might want to consider an approach which has the same guarantee of freeing your objects but which is much cleaner (and has other benefits), such as my own AutoFree() implementation.

With AutoFree() your code could then be written:

aObject := TObject.Create;
AutoFree(@aObject);

aObject.AProcedure();

or alternatively, since the implementation uses a reference to a reference (to enable auto-NIL'ing as well as Free'ing), you can even pre-register your references that you wish to be AutoFree'd to keep such housekeeping declarations away from your business logic and keep the real "meat" of your code as clean as possible (this is especially beneficial when potentially multiple objects need to be Free'd):

AutoFree(@aObject1);
AutoFree(@aObject2);

aObject1 := TObject.Create;
aObject1.AProcedure();

// Later on aObject2 is (or may be) also created
 :

Not shown in my original posting is a later addition to the mechanism to support registering multiple references in a single AutoFree() call, but I'm sure you can figure out the changes needed to support this on your own, if you wish to be able to do this:

AutoFree([@aObject1, @aObject2]);

aObject1 := TObject.Create;
aObject1.AProcedure();

// Later on aObject2 is (or may be) also created
 :
孤檠 2024-09-09 21:08:33

即使强烈建议这样做,我也不会总是这样做。

我使用或不使用 try/finally 的规则:

  • 该对象崩溃和烧毁的机会。
  • 创建对象的次数。如果我知道你的对象很少被创建(在应用程序生命周期中不超过 5-6 次),我可以忍受 20KB 内存泄漏 - 以防它在没有被释放的情况下“死亡”。
  • 对象崩溃时泄漏的内存量。
  • 代码复杂度。 Try/ except 使代码看起来非常难看。如果有 5 行程序,我总是使用 try/ except。
  • 应用程序文件跨度。如果您的应用程序需要运行几天,那么我绝对想释放任何内存,否则泄漏将会累积。

唯一难以做出决定的地方是当您在创建对象数千次(例如在循环中)时需要性能时。在这种情况下,如果对象正在执行简单的任务并且有很小的机会看到它崩溃,我不会使用 try/ except 。

Even if it is much recommended to do that, I won't always do it.

My rules of using or not using the try/finally:

  • The chance for that object to crash and burn.
  • Number of times the object is created. If I know that your object is created rarely (not more than 5-6 time in application life time) I can live with a 20KB memory leak - in case it 'dies' without being freed.
  • Amount of leaked memory in case the object crashes.
  • Code complexity. Try/except is making the code to look really ugly. If there is a 5 lines procedure, I always use try/except.
  • Application file span. If your application needs to run for days, then I definitively want to free ANY piece of memory else the leak will accumulate.

The only place where is difficult to make a decision is when you need performance while creating the object thousands of times (in a loop for example). In this case, I don't use try/except if the object is performing simple tasks and there is a small chance to see it crashing.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文