我在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
事件将永远不会运行,因此树将知道该节点尚未初始化。未初始化的节点不会最终确定,因此您无需担心。