Delphi中明确表达所有权

8
我主要是C ++程序员,我习惯于使用类模板,例如std::unique_ptr、std::shared_ptr等来表示对象的所有权。Delphi的标准库中是否有类似的内容?在编写代码时,是否有任何表达对象所有权的最佳实践应该遵循?
编辑:自从C++11成为标准以来,出现了两个轻量级辅助类std::shared_ptr和std::unique_ptr。
如果我创建一个类型为std::shared_ptr的变量,则表示具有共享所有权的int指针:底层是引用计数的,当引用计数达到零时,指针会自动释放。这种类型表示一种“共享所有权”,许多对象共享销毁资源的责任。
相比之下,std::unique_ptr表示单一所有权。当unique_ptr超出范围时,资源会自动释放。std::unique_ptr不能被复制:在任何时候只能有一个对象拥有此资源,并且只有一个对象负责清理对象。
将这些轻量级类与裸指向int的指针进行对比,裸指针可以表示共享所有权、唯一所有权,也可以只是对某个对象的引用!类型什么都不告诉你。
我的问题是:由于Delphi支持持有对对象的引用,是否有机制明确地说明“我是这个对象的唯一所有者,在我完成后,我将释放它”,而不是“我仅仅是为了与之交互而保留对此对象的引用,但其他人将清理它”或“我与许多其他对象共享此对象,谁最后拥有它就负责清理它。”
我知道Collections.Generics有不同的集合,例如TList和TObjectList,其中TObjectList将释放其中存储的成员,但TList不会。您可以说TObjectList“拥有”其元素,而TList则没有。这实际上是我的问题的本质。在设计自己的类时,是否有直接表达这些所有权问题的方法?或者有任何常见的最佳实践/命名约定吗?

我不知道那些模板是做什么的,所以很难将它们的功能与 Delphi 中其他模板进行比较。也许您可以提供更多有关您希望从 Delphi 中获得的行为的详细信息。此外,请包括您感兴趣的特定 Delphi 版本的标签,以便更好地回答问题。 - AlexSC
谢谢,我添加了一些类的解释,并进一步阐述了我的问题。 - bstamour
你是指 std::shared_ptr<int>,对吧?我猜括号被标记语言搞乱了。 - Uli Gerhardt
是的,没错。标记语言吃掉了我的模板括号。 - bstamour
1
也许可以看一下这里:https://dev59.com/1kfRa4cB1Zd3GeqP8FE6 或者这里:http://blog.barrkel.com/2008/09/smart-pointers-in-delphi.html - J...
显示剩余3条评论
3个回答

4
我不知道有任何语言结构可以帮助,也不知道有任何“标准命名约定”。
但是,很久以前,我采用了以下命名约定,以便更轻松地检查类是否正确清理自己的内容:
  • 按照标准的Delphi约定,所有字段名称都以“F”开头。
  • 对象引用(类具有/承担生命周期管理责任)以“FMy”开头。
  • 应通过在析构函数中将引用设置为nil显式释放类应释放的接口引用(出于性能、打破循环依赖等原因)以“FMi”开头
这非常简单粗暴,但它有效,当你浏览长时间未见的代码时,有助于防止那些“等等,那个引用应该被释放或置空吗?”的搜索。

这是个好主意。谢谢。我已经遵循了整个F前缀的规则,但我的代码库又老又乱!也许当我需要重构某些部分时,我会开始强制执行像你这样的命名约定。 - bstamour

2
“std::unique_ptr” 不能被复制:一次只能有一个对象拥有该资源。
在 Delphi 语言中,没有类型或机制可以防止共享“所有权”。任何引用的副本都可以制作。(即:Delphi 中没有任何可以阻止赋值的东西,正如 David 所说。)
当“unique_ptr”超出范围时,资源会自动释放。
在 Delphi 中,这只能通过接口实现。Delphi 没有垃圾回收器。
并且只有一个对象负责清理对象。您必须自己执行清理任务或将其委托给(另一个)框架。例如,默认的 Delphi VCL 类“TComponent”实现了可选地交换/控制的自动所有权(和销毁),可以通过“RemoveComponent”和“InsertComponent”进行。
设计自己的类时,有没有直接在语言中表达这些所有权问题的方法?或者在开发人员中是否存在任何最佳实践/命名约定?不完全是主题,但肯定相关:有多个“单例”设计模式实现,强制对象单次创建。关于命名约定:术语“Owner”(或您自己示例中的“OwnsObjects”)明确表达了所有权,即所有者将在必要时负责销毁。因此,使用表单作为所有者(按钮默认构造函数的单个参数)创建的按钮无需手动销毁。

请注意,Delphi >= XE4 的移动编译器实现了自动引用计数:http://docwiki.embarcadero.com/RADStudio/XE4/en/Automatic_Reference_Counting_in_Delphi_Mobile_Compilers - Jeroen Wiert Pluimers
谢谢这个。我的动机是,我经常持有对另一个对象的引用,而我只是“查看”它,并且不应该在析构函数中销毁它,因为它由我的程序中的另一个对象拥有,最终将调用free释放它。我想这将归结于命名约定。 - bstamour

1
Delphi和C++的概念在许多场合下不同。两种语言都是第三代语言,但Delphi喜欢在比C++更高的抽象层次上工作。例如,Delphi支持指针,但与C++中的引用概念不完全相同,因此很少使用指针。
在Delphi中,对象变量实际上是引用(或在更低的抽象层次上,它们是指针)。在C++中,当您声明一个对象变量时,构造函数会立即被调用,在Delphi中则不是,您必须在给定的时刻调用它,这将分配内存并运行构造函数。因此,C++和Delphi中对象的内存管理受到不同生命周期的制约。
所有这些只是为了告诉你,Delphi中的内存管理设计风格与C ++不同。这就是为什么Delphi没有任何帮助类可以精确地做到你想要的。但是,Delphi提供了一个名为Interfaces的概念,在C++中不存在(至少在我很久以前使用C++时不存在)。接口类似于抽象类,因为它们没有代码。您必须向接口提供一个类实现者,该类将提供代码。然而,Interfaces提供了引用计数内存管理,我认为这非常接近您要寻找的。

因此,我的答案是:Delphi提供给您的最接近内存管理方面的语言结构是接口。因此,我建议您至少学习一下它,以得出自己的结论。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接