我在C++ Builder中使用VirtualTreeView并将其与如下结构一起使用:

struct TVTNodeData
   {
   int Index;
   UnicodeString Caption;
   }

我使用具有以下内容的循环来预填充树的节点:
TVirtualNode *Node = VTree->AddChild(NULL);
pNode = (TVTNodeData *)VTree->GetNodeData(Node);
pNode->Index = 1;
pNode->Caption = "Whatever";

我注意到,即使我清除树并重新加载,应用程序的内存仍在不断增加(内存泄漏)。本页-http://www.remkoweijnen.nl/blog/2010/06/09/memory-leaks-when-using-virtual-treeview-component/建议在OnFreeNode事件中执行Finalize()。目前很好。

但是C++中没有Finalize()。我在pNode->Caption=""事件中尝试过OnFreeNode,并且不再为内存分配过多的内存,但是仍然有点。我认为即使将其清空(引用计数> 0),也可能还有对UnicodeString的引用。

如何在C++中为UnicodeString释放OnFreeNode事件中的节点数据?我知道UnicodeString一直分配到所有引用计数都为零为止-那么如何强制引用计数变为零?

另外,如果节点是在OnNodeInit中分配的,该怎么办-在OnFreeNode事件中是否同样适用?

如果TVTNodeData结构是纯虚拟的,该节点将永远不可见,也不会使用AddChild或OnNodeInit进行初始化,然后需要Finalize,那么该结构甚至存在于内存中怎么办?

更新:后来我发现我不正确地测量内存使用情况,对于将字符串设置为空字符串的字符串,确实足以清除内存数据。但是-正如Rob Kennedy在下面的回答中所建议的那样,调用struct〜destructor甚至更好,等效于Finalize,并且由于它清除了整个结构(如果您包含更多的字符串),因此也更加容易。

最佳答案

Delphi的Finalize具有释放记录中所有编译器管理的类型的效果。在C++中,通常是类型的析构函数的工作。在OnFreeNode事件处理程序中,直接调用数据类型的析构函数:

TVTNodeData* const pNode = static_cast<TVTNodeData*>(Sender->GetNodeData(Node));
pNode->~TVTNodeData();

这将调用UnicodeString对象的析构函数,该析构函数将释放关联的字符数据。当树控件为节点分配TVTNodeData时,它与TVirtualNode对象本身位于同一块内存中,因此您不能只调用delete

树控件使用全零位初始化数据。如果数据中的对象不是正确的初始化(从形式上正确来说,包括所有非POD类型),则应在OnInitNode事件中调用数据的构造函数。使用新的展示位置可以做到这一点。例如:
TVTNodeData* const pNode = static_cast<TVTNodeData*>(Sender->GetNodeData(Node));
new (pNode) TVTNodeData();

这将调用TVTNodeData成员的构造函数,而无需为其他TVTNodeData实例分配内存。

如果一个节点从未被初始化,那么它也不会被终结。 OnInitNode事件将永远不会运行,因此树将知道该节点尚未初始化。未初始化的节点不会最终确定,因此您无需担心。

10-08 09:32