【问题标题】:How to make destructors be called on going out of scope?如何在超出范围时调用析构函数?
【发布时间】:2020-10-28 22:36:58
【问题描述】:

开启 GC 后,是否可以在超出范围后立即调用析构函数。

是否有可能在超出所有对象[任何类型]的范围时调用析构函数?

为什么超出范围时不调用析构函数?

在this 发布“scope 在局部变量上”被称为“不安全”。为什么它被认为是不安全的?
弃用该功能的理由是

范围作为类型约束是语言中的一个怪癖,没有令人信服的用例。

没有令人信服的用例?就像将对象放在堆栈上一样(这个功能就是这样做的,对吗?)并不比堆上快。

【问题讨论】:

    标签: d


    【解决方案1】:

    通常,D GC 在从函数返回时不会调用析构函数。 GC 在分配发生时触发,并且在调用GC.collect 时触发。

    D 具有scoped,它将非 RAII 类型包装在具有 RAII 行为的结构中。这样,结构的析构函数可以负责清理内存。请注意,虽然这通常有效,但仍有一些特殊情况和需要注意的事情,GC 将自动处理。这允许在离开作用域时在任何对象上调用析构函数。

    当它们离开作用域时,并不是所有对象都调用析构函数,因为可能存在对对象的其他引用。考虑这段代码:

    int* global;
    
    void func() {
        int* p = new int;
        global = p;
    }
    

    如果p 指向的int 在func() 返回时被破坏,那么global 将指向被破坏的内存。

    你链接的文章已经快十年了,而D在此期间发生了一些变化。 scope 现在具有更好的语义,这有意义地限制了您可以对标记为这样的变量执行的操作:

    scope 局部变量可能仍被分配给全局变量,这似乎是一个疏忽。如果找不到现有的错误,我会提交错误。

    【讨论】:

    • 我了解可能存在对对象的引用。但是为什么不能在释放最后一个引用的那一刻调用析构函数呢?
    • 这是可能的,通常称为引用计数。 GC 处理的引用计数存在一些问题,就像 GC 处理的一些问题 RC 做得更好一样。 Bacon 等人在 A Unified Theory of Garbage Collection 中对此进行了介绍。 D 选择了 GC,但在某种程度上可以使用 RC(参见 D 中的refCounted。
    • 文件之类的东西呢?由于GC调用了析构函数,这意味着文件的句柄也被GC关闭了吗?这不是疯了吗?
    【解决方案2】:

    开启 GC 后,是否可以在超出范围后立即调用析构函数。

    是否有可能在超出所有对象[任何类型]的范围时调用析构函数?

    为堆栈上的任何对象调用析构函数,这适用于分配为scope Foo f = new Foo() 的结构和类。

    在这篇文章中,“局部变量的作用域”被称为“不安全”。为什么它被认为是不安全的?

    因为可以转义对堆栈分配实例的引用,这可能比当前函数调用更有效。 (但 DIP 25 / 1000 可以检测到大部分此类问题)

    弃用该功能的理由是 [...] 没有令人信服的用例?就像将对象放在堆栈上一样(这个功能就是这样做的,对吗?)并不比堆上快。

    仅不推荐使用附加到类型声明的scope。


    但作为一般建议,如果您的对象需要确定性销毁(例如File 句柄),请使用结构。

    【讨论】:

      猜你喜欢
      • 2012-06-14
      • 2012-09-26
      • 2016-09-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多