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

sha*_*eMe 1 garbage-collection destructor idisposable c++-cli 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
    }
};
Run Code Online (Sandbox Code Playgroud)

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

Dan*_*ker 5

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

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

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

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

这也意味着它们在构造时会自动默认Workspace构造,这可能会gcnew在你的手写构造函数中为你节省很多东西.

也就是说,如果你说:

Form WorkspaceUI;
Run Code Online (Sandbox Code Playgroud)

那你不需要说:

WorkspaceUI = gcnew Form();
Run Code Online (Sandbox Code Playgroud)

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

你也不需要处理/删除任何东西.

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

Form.Controls->Add(WorkspaceSplitter);
Run Code Online (Sandbox Code Playgroud)

更新:

在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);
Run Code Online (Sandbox Code Playgroud)

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

  • 你无意中持有它的引用(或它分配的东西).请参阅我对这个问题的回答:C#WPF中的内存泄漏(它没有任何与C#或WPF有关的内容,只是交互式使用调试器的问题)
  • 您需要调用Dispose在类中分配的一个或多个对象.

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

因此,如果删除句柄,则会调用Dispose它指向的对象.

或者,如果(如上所述)您只是拥有成员对象,则甚至不需要明确删除.当外部包含类被破坏时(即某些东西调用它的Dispose方法),它将自动调用Dispose任何需要它的成员对象.