在C ++ / CLI中正确对象处理

时间:2011-10-22 08:47:26

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

考虑以下课程:

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

1 个答案:

答案 0 :(得分:5)

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

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

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

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

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

也就是说,如果你说:

Form WorkspaceUI;

然后你不需要说:

WorkspaceUI = gcnew Form();

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

您也不需要处置/删除任何内容。

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

Form.Controls->Add(WorkspaceSplitter);

<强>更新

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

相应地,还需要一种获取对象句柄的方法。要获取指向本机对象的指针,我们前缀为&。要获取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