如何编写适当的析构函数和终结符?

Sar*_*ien 7 destructor c++-cli finalizer

我试图弄清楚如何在C++/CLI中正确清理我的对象之后.

我已经阅读或浏览了这两篇文章(,)并查看了标准并查看了其他一些问题,特别是这一个问题.

我有各种各样的信息:

  1. 终结器应该清理非托管资源(因此当对象被垃圾收集时,所有内容都会被清除.
  2. 析构函数应该清理托管资源(删除Foo或Foo.Dispose()?)并调用终结器(根据1)
  3. 既析构函数和终结器可以被多次调用(参见3的8.8.8第26页端)
  4. 如果调用析构函数,则不再调用终结器(根据1)(不是通过CLR,也就是说,您仍然可以自己调用它)
  5. 析构函数将调用基类析构函数(参见第3页,第25页)
  6. 具有终结器的类应始终具有析构函数(可能是为了确定性地清理非托管资源)
  7. 对终结器的调用不会调用基类终结器(3 19.13.2,第131页)

但也有很多混乱,部分是由于这一事实造成的

  1. 终结器在C#中称为析构函数
  2. 析构函数在内部生成Dispose和Finalize方法(不确定Finalize),但Finalize方法不是终结器
  3. 析构函数的语义在C++中是不同的,并且通常具有确定性清理和垃圾收集的复杂性

我想要的答案是一个类的例子,它包含它可能包含的所有不同类型的数据(托管,非托管,托管但是一次性的,你能想到的其他任何东西)和正确编写的析构函数和终结器.

我有两个更具体的问题:

  1. 通过只有一个bool hasBeenCleanedUp成员处理多次被调用的可能性并使析构函数/终结器中的整个代码以此为条件,是否可以接受?
  2. 什么样的数据只能由析构函数清理,但不能在终结器中清理,因为它可能已经被gc清理了?

Pra*_*eek 5

对您的问题不是一个完整的答案,但是太长,无法发表评论.

完全托管的世界中,每个对象只引用托管对象,不需要终结器或析构函数,因为唯一的资源是内存,GC负责处理它.

当您引用非托管资源时,您有责任在不再需要它们时释放它们.

因此,您需要实现专用的清理代码.

有两种可能性:

  • 你知道什么时候你不再需要非托管资源,所以你可以确定地运行你的清理代码,这是通过析构函数/ Dispose实现的

  • 您不知道何时不再需要这些资源,因此您在最后可能的时刻推迟清理,当GC收集包装资源的对象时,这是通过终结器实现的

您认为在第一种情况下更好,因为您不会消耗超过您需要的内存,并且可以避免GC进程的额外开销.

您通常实现这两种方法,因为实例的生命周期可能因使用情况而异.

CLR级别,没有确定性清理,只有终结器.

语言/ API级别,支持确定性清理:

  • 在本机C++中,您在退出作用域或"删除"时调用了析构函数

  • 在.Net世界中,你有Dispose模式

  • 在纯托管C++/CLI中,世界析构函数映射到Dispose

当您有机会确切知道何时可以运行清理代码时,您可以调用(或让基础结构调用)析构函数.清理完成后,您可以清除所有完成过程,以便在下一个GC时立即收集对象.

关于你的第一个要点的一些澄清:

  1. 析构函数也负责清理非托管资源; 它可以调用终结器,如果它是你清理代码的因素.

  2. 从技术上讲,它们可以在逻辑上用一个简单的布尔保护来阻止它

  3. 是的,因为所有的清理都应该完成,所以你要求CLR不要完成对象

  4. 是的,因为基类知道它分配了哪些资源

  5. 是的,这是用于确定性清理

  6. 你应该确保它是这样的

其他人:

  1. 是的~MyClass映射到Finalize方法的覆盖

  2. 如上所述,析构函数映射到Dispose,但您应该自己实现终结器:!MyClass

  3. 简介:C++析构函数和Dispose模式用于确定性清理,C#析构函数,C++/CLI终结器用于由GC触发的非确定性清理.