代码之家  ›  专栏  ›  技术社区  ›  sharkin

C-超出范围时是否立即销毁物品?

  •  10
  • sharkin  · 技术社区  · 15 年前

    我能相信一个对象被破坏,它的析构函数在C中超出范围后立即调用吗?

    我认为这应该是因为许多常见的编码实践(例如事务对象)都依赖于这种行为,但是我不太习惯于使用垃圾收集,对这种语言通常的行为也没有多少了解。

    谢谢。

    6 回复  |  直到 7 年前
        1
  •  27
  •   Saurabh R S    7 年前

    不,.net,因此c依赖于垃圾收集内存管理。因此,在GC发现销毁对象是正确的之前,不会调用析构函数(在.NET中称为finalizers)。

    另外:C中的大多数“常规”对象没有析构函数。如果需要析构函数模式,应该实现 IDisposable interface Dispose Pattern . 对于可弃对象,还应确保调用dispose方法,或者使用 using keyword 或者直接调用方法。

    进一步(希望)澄清:确定性处理在.NET中很有用,例如,当您需要显式释放不由.NET运行时管理的资源时。这类资源的例子有文件句柄、数据库连接等。通常重要的是,一旦不再需要这些资源,就立即释放它们。因此,我们不能等待gc释放它们。

    为了在.NET GC的非确定性世界中获得确定性的处理(类似于C++的范围行为),.NET类依赖IDISPOLIDLE接口。从 处置模式 ,下面是一些示例:

    首先,实例化一个可释放资源,然后让对象超出范围,这将由gc来处理对象:

    1.    {
    2.       var dr = new DisposableResource();
    3.    }
    

    为了解决这个问题,我们可以明确地处理对象:

    1.    {
    2.       var dr = new DisposableResource();
    3.
    4.       ...
    5.
    6.       dr.Dispose();
    7.    }
    

    但如果2号线和6号线之间出了问题怎么办?不会调用Dispose。为了进一步确保dispose最终将被调用,而不考虑任何异常,我们可以执行以下操作:

    1.    var dr = new DisposableResource();
    2.    try
    3.    {
    4.       ...
    5.    }
    6.    finally
    7.    {
    8.       dr.Dispose();
    9.    }
    

    由于这种模式是经常需要的,所以c包含using关键字来简化事情。以下示例与上述示例相同:

    1.    using (var dr = new DisposableResource())
    2.    {
    3.       ...
    4.    }
    
        2
  •  9
  •   AgentConundrum    15 年前

    不安 对象 实际上并没有“超出范围”,对它的引用(即用于访问它的变量)确实是这样。

    一旦不再有对给定对象的引用,该对象将成为 合格的 用于垃圾收集(GC)。每当gc决定需要回收不再被引用的对象的空间时,就会调用对象终结器。

    如果您的对象是一个资源(例如文件句柄、数据库连接),它应该实现IDisposable接口(它要求对象实现 Dispose() 清理任何打开连接的方法等)。在这种情况下,您的最佳实践是创建对象作为 using 块,这样当此块完成时,应用程序将自动调用对象 处置() 方法,它将负责关闭文件/db connection/whatever。

    例如

    
    using (var conn = new DbConnection())  
    { 
       // do stuff with conn  
    } // conn.Dispose() is automatically called here.  
    
    

    这个 使用 块只是一些语法糖,它将您的交互与 conn 对象中的 try 块,连同 finally 只调用 conn.Dispose()

        3
  •  4
  •   Joren    15 年前

    C语言中没有一个类似C++的析构函数。(C语言中析构函数有一个不同的概念,也称为终结器,它使用与C++析构函数相同的语法,但它们与销毁对象无关。它们旨在为非托管资源提供清理机制。) 垃圾收集器将清理对象 在他们不再被引用之后 . 不是马上,也没有办法保证。

    幸运的是,你也没有真正的理由要保证这一点。如果您需要内存,那么gc将回收它。如果你没有,为什么要关心周围是否还有垃圾对象?这不是内存泄漏:gc仍然可以找到它并随时清理它。

        4
  •  4
  •   Joey Gumbo    15 年前

    不,这不能保证。类似于Java等语言,在C语言中,垃圾回收器在需要时运行(即当堆变得太满)。但是,当您的对象实现 IDisposable ,即他们有一个 Dispose() 方法和它必须被调用,然后你可以利用 using 关键词:

    using (var foo = new DisposableObject()) {
        // do something with that
    }
    

    那样 处置() 离开时会立即打电话 使用 块。

    注: 不可分的 在许多类型中都可以找到,最显著的是gdi+,还有数据库连接、事务等,所以这里可能是正确的模式。

    注2:以上区块的幕后将被翻译为 try / finally 块:

    var foo = new DisposableObject();
    try
    {
        // do something with that
    }
    finally
    {
        foo.Dispose();
    }
    

    但是这个翻译是由编译器完成的,而且非常方便,不忘调用 处置()

        5
  •  0
  •   Toad    15 年前

    我认为你不应该这样依赖垃圾收集员。即使你扣除他们是如何操作的,很可能在下一个版本中他们已经重新实现了它。

    在任何情况下,对象在您取消引用时都不会被垃圾回收。通常,在达到某个阈值后再将其释放。

    特别是在Java程序中,当您查看任务管理器上的内存消耗时,这一点非常明显。它不断地生长,每一分钟它都会突然再次下降。

        6
  •  0
  •   Andrey Taptunov    15 年前

    不。如果您参考cli规范(关于终结器的第8.9.6.7页) http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-335.pdf 您可以找到以下内容

    CLI 应该 确保在实例成为 无法接近。同时依靠记忆压力 触发器终结是可以接受的,实现者应该考虑使用 韵律学

    但绝对不能。