【问题标题】:Proper Object Disposal In C++/CLIC++/CLI 中正确的对象处理
【发布时间】:2011-12-13 02:09:52
【问题描述】:

考虑以下类:

public ref class Workspace
{
protected:
    Form^                 WorkspaceUI;
    SplitContainer^       WorkspaceSplitter;
    AvalonEditTextEditor^ TextEditor;
    ScriptOffsetViewer^   OffsetViewer;
    SimpleTextViewer^     PreprocessedTextViewer;

    ListView^             MessageList;
    ListView^             FindList;
    ListView^             BookmarkList;
    ListView^             VariableIndexList;
    TextBox^              VariableIndexEditBox;
    Label^                SpoilerText;

    ToolStrip^            WorkspaceMainToolBar;
    ToolStripButton^      ToolBarNewScript;
    ToolStripButton^      ToolBarOpenScript;
    ToolStripButton^      ToolBarPreviousScript;
    ToolStripButton^      ToolBarNextScript;
    ToolStripSplitButton^ ToolBarSaveScript;
    ToolStripDropDown^    ToolBarSaveScriptDropDown;
    ToolStripButton^      ToolBarSaveScriptNoCompile;
    ToolStripButton^      ToolBarSaveScriptAndPlugin;
    ToolStripButton^      ToolBarRecompileScripts;
    ToolStripButton^      ToolBarCompileDependencies;
    ToolStripButton^      ToolBarDeleteScript;
    ToolStripButton^      ToolBarNavigationBack;
    ToolStripButton^      ToolBarNavigationForward;
    ToolStripButton^      ToolBarSaveAll;
    ToolStripButton^      ToolBarOptions;

    ArbitraryCustomClass^ CustomClassInstance;

public:
    Workspace()
    {
        WorkspaceUI = gcnew Form();
        WorkspaceSplitter = gcnew SplitContainer();
        // ...
        Form->Controls->Add(WorkspaceSplitter);
        // ...

        WorkspaceUI->Show();
    }

    ~Workspace
    {
        // dispose stuff here
    }
};

处置上述类的实例以使其所有内存在下次回收期间由 GC 回收的最有效和优雅的方法是什么?我是否需要对每个成员显式调用 delete 和/或将它们重置为 nullptr

【问题讨论】:

    标签: garbage-collection c++-cli destructor idisposable delete-operator


    【解决方案1】:

    注意。您可能不需要做任何事情。当指向对象的引用不再存在时,GC 会回收对象的内存。

    您只需要在对象实现IDisposable 时显式回收。在 C++/CLI 中,这映射到析构函数。

    因此,如果您分配的所有对象都不需要处理,则可以忽略此答案的其余部分。但假设他们这样做......

    从每个字段中删除^,它们将被自动回收。

    这也意味着它们将在构造 Workspace 时自动默认构造,这可以为您在手写构造函数中节省大量 gcnew 内容。

    也就是说,如果你说:

    Form WorkspaceUI;
    

    那你就不用说了:

    WorkspaceUI = gcnew Form();
    

    编译器已经为你生成了 - 想象一下它被插入到你的构造函数的开头。

    您也不需要处置/删除任何东西。

    最后,您需要使用. 而不是-> 来访问您以这种方式声明的对象的成员:

    Form.Controls->Add(WorkspaceSplitter);
    

    更新:

    在 C++/CLI 中,引用类的句柄使用 ^ 声明,这类似于使用 * 声明指向本机类的指针的方式。

    同样,需要有一种方法来获取对象的句柄。为了获得指向本机对象的指针,我们以& 为前缀。为了获得一个 ref 对象的句柄,我们以% 为前缀。例如:

    ref class Fred { };
    
    // function that accepts a handle
    void ping(Fred ^h) { }
    
    // Elsewhere... declare object of type Fred
    Fred f;
    
    // Get handle to pass to function
    ping(%f);
    

    如果重复创建和删除类的对象导致内存不足,则有两种可能:

    • 您无意中持有对它的引用(或它分配的东西)。请参阅我对这个问题的回答:Memory Leaks in C# WPF(它实际上与 C# 或 WPF 没有任何特定关系,只是交互使用调试器的问题)
    • 您需要对您在类中分配的一个或多个对象调用Dispose

    如果是后者,在 C++/CLI 中内置支持自动调用 Dispose - C++/CLI 将一次性对象视为带有析构函数的 C++ ref 类。

    所以如果你删除一个句柄,你就是在它指向的对象上调用Dispose

    或者如果(如我上面所建议的)您只是拥有成员对象,您甚至不需要显式删除。当外部包含类被破坏时(即调用它的Dispose 方法),它会自动在任何需要它的成员对象上调用Dispose

    【讨论】:

    • 在这种情况下,如何将对象传递给需要其句柄的函数?如:标记 SomeText; Form.Controls->Add(SomeText); // 引发编译器错误
    • 此外,继续创建和销毁上述类只会导致 System.OutOfMemoryException - GC 似乎没有回收对象的内存。这使我相信它甚至可能对过去的破坏有强烈的参考意义。在我的代码中,所有类的实例都被分配并直接添加到列表中。销毁时,实例会从其中删除。这是每个实例在根中获得的唯一引用。然而,类的各个成员都有多个内部引用。
    • @shadeMe:您需要在Workspace 对象本身上调用Dispose,以触发所有拥有对象的处置。
    • C++/CLI 中从来没有 Dispose 方法。编译器不允许显式声明一个对象,如果您尝试对一个对象调用 Dispose,编译器会拒绝该对象存在。正确的做法是使用delete h 其中h 是对象的句柄(我假设这是@shadeMe 的“销毁”的意思)或声明一个对象(不是句柄)因此它被自动销毁当它离开作用域或封闭对象破坏时。
    猜你喜欢
    • 2015-04-20
    • 2011-07-05
    • 1970-01-01
    • 2012-10-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多