C++/CLI 资源管理混淆

18

我对C++/CLI中的资源管理感到非常困惑。我以为自己已经掌握了这个知识点,但是在查看头文件时,我发现了auto_gcroot<T>类,于是进行了谷歌搜索、整整一天的阅读文档,但现在仍然感到困惑。因此我决定向社区求助。

我的问题涉及auto_handle/stack语义和auto_gcroot/gcroot之间的区别。

  1. auto_handle: 我理解的是它可以清理由托管函数创建的托管对象。但我困惑的是,垃圾回收器难道不应该替我们完成这个工作吗?这不是托管代码的全部意义吗?更具体地说:

    //Everything that follows is managed code
    void WillThisLeak(void)
    {
        String ^str = gcnew String ^();
        //Did I just leak memory? Or will GC clean this up? what if an exception is thrown?
    }
    
    void NotGoingToLeak(void)
    {
        String ^str = gcnew String^();
        delete str;
        //Guaranteed not to leak, but is this necessary? 
    }
    
    void AlsoNotGoingToLeak(void)
    {
        auto_handle<String ^> str = gcnew String^();
        //Also Guaranteed not to leak, but is this necessary? 
    }
    
    void DidntEvenKnowICouldDoThisUntilToday(void)
    {
        String str();
        //Also Guaranteed not to leak, but is this necessary? 
    }
    

    如果这是替代C# using关键字的东西,并且只建议在使用资源密集型类型(如位图)时使用,那么这将对我有意义,但文档中没有提到,所以我担心自己一直在泄漏内存。

  2. auto_gcroot

我能把它作为参数传递给本地函数吗?复制时会发生什么?

    void function(void)
    {
        auto_gcroot<Bitmap ^> bmp = //load bitmap from somewhere
        manipulateBmp(bmp);
        pictureBox.Image = bmp;  //Is my Bitmap now disposed of by auto_gcroot?
    }

    #pragma unmanaged

    void maipulateBmp(auto_gcroot<Bitmap ^> bmp)
    {
        //Do stuff to bmp
        //destructor for bmp is now called right? does this call dispose?
    }

如果我使用 gcroot 代替 auto_gcroot,这个代码会起作用吗?

此外,auto_handle 和 auto_gcroot 有什么优点?它们似乎做了类似的事情。

我一定误解了某些东西,所以需要一个好的解释。另外,关于这些类型的正确使用方法、学习这些内容的地方以及更多的最佳实践/可以找到它们的地方的任何指导都将不胜感激。

非常感谢, Max


我从不使用这里的大多数表单:我总是使用托管对象的引用,并且我总是让GC为我清理它们。我有时会使用gcroot,但很少(当我想将.NET闭包/委托包装到C++函数对象中并确保保持句柄尽可能长时间时)。 - Alexandre C.
这是一个相当错误的尝试,试图将auto_ptr<>引入托管世界。就像STL/CLR库一样愚蠢。最好的做法就是忘记它们。 - Hans Passant
1个回答

28
  1. 记住,对托管对象调用delete等同于在C#中调用Dispose。所以你是正确的,auto_handle让你做到了在C#中使用using语句的那些事情。它确保delete在作用域末尾被调用。因此,如果不使用auto_handle,则不会泄漏托管内存(垃圾收集器会处理),您只是未能调用Dispose。如果处理的类型不实现IDisposable,则无需使用auto_handle。

  2. 当您想要在本地类中保存托管类型时,可以使用gcroot。您不能直接使用帽子符号^在本地类型中声明托管类型。必须使用gcroot。这是一个"垃圾回收根"。因此,只要gcroot(本地对象)存活,垃圾收集器就无法收集此对象。当gcroot被销毁时,它释放了引用,垃圾回收器就可以收集该对象(假设它没有其他引用)。像上面一样在方法中声明独立的gcroot-只需使用帽子符号^语法即可。

那么什么时候使用auto_gcroot呢?当您需要在本地类中保存托管类型,并且该托管类型恰好实现了IDisposable时,将使用auto_gcroot。在销毁auto_gcroot时,它会执行两个操作:对托管类型调用delete(这相当于调用Dispose-不释放内存),并释放引用(因此可以对该类型进行垃圾回收)。

希望对你有所帮助!

一些参考资料:

http://msdn.microsoft.com/en-us/library/aa730837(v=vs.80).aspx

http://msdn.microsoft.com/en-us/library/481fa11f(v=vs.80).aspx

这个链接是关于使用Visual Studio 2005的C++语言工具开发.NET应用程序的文档。它包含了有关各种.NET技术、架构和编程概念的信息,例如CLR、Assemblies和Managed C++等。

http://www.codeproject.com/Articles/14520/C-CLI-Library-classes-for-interop-scenarios

这篇文章介绍了如何在C++和CLI之间进行互操作,以及如何创建和使用C++/CLI库类来实现这种互操作性。其中还包括了一些互操作的示例代码,可以帮助读者更好地了解和掌握这种技术。

3
终于有人讲话有道理了! - Max Ehrlich

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