3

我在 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,内存不再分配得那么剧烈,但它仍然有点。我认为即使 UnicodeString 被清空(引用计数> 0),也可能会留下对 UnicodeString 的引用。

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

另外,如果节点被分配了怎么办OnNodeInit- 这同样适用于OnFreeNode事件吗?

如果 TVTNodeData 结构是纯虚拟的 - 节点既不可见,也不使用 AddChild 或 OnNodeInit 初始化,然后需要 Finalize,那么该结构是否存在于内存中?

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

4

2 回答 2

8

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

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

这将调用UnicodeString对象的析构函数,这将释放相关的字符数据。当树控件TVTNodeData为一个节点TVirtualNode分配delete.

树控件使用所有位为零来初始化数据。如果您的数据中有一个不是正确初始化的对象(正式正确,包括所有非 POD 类型),那么您应该在事件中调用数据的构造函数。OnInitNode使用placement new来做到这一点。例如:

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

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

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

于 2012-04-17T16:19:30.287 回答
2

我认为并非所有节点都经过验证,可能是因为它们没有显示。尝试在 AddChild 之后调用 ValidateNode。

于 2012-04-17T13:59:19.647 回答