1

考虑下面的类:正确的对象处理在C++/CLI

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回收在下一次收集?我是否需要拨打删除明确在每个成员和/或重置他们到nullptr

回答

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++引用类与析构函数。

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

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

+0

在这种情况下,如何将对象传递给需要其句柄的函数?如: Label SomeText; Form.Controls-> Add(SomeText); //引发编译器错误 – shadeMe

+0

此外,继续创建和销毁上述类只会导致System.OutOfMemoryException - GC似乎不会回收对象的内存。这使我相信,它可能有强烈的参考甚至过去的破坏。在我的代码中,所有类的实例都被分配并直接添加到列表中。销毁时,实例将从相同的位置移除。这是每个实例在根中获取的唯一参考。然而,这个班的不同成员有多个内部参考。 – shadeMe

+0

@shadeMe:您需要在Workspace对象本身调用Dispose来触发处理所有拥有的对象。 –