Question

I use a VirtualTreeView in C++ Builder and use it with structure like this:

struct TVTNodeData
   {
   int Index;
   UnicodeString Caption;
   }

I pre-fill nodes of the tree using loop that has this:

TVirtualNode *Node = VTree->AddChild(NULL);
pNode = (TVTNodeData *)VTree->GetNodeData(Node);
pNode->Index = 1;
pNode->Caption = "Whatever";

I noticed that the memory for the application is constantly increasing (memory leak) even though I clear tree and reload it. This page - http://www.remkoweijnen.nl/blog/2010/06/09/memory-leaks-when-using-virtual-treeview-component/ recommends to do Finalize() in OnFreeNode event. Good so far.

But there is no Finalize() in C++. I tried pNode->Caption="" in OnFreeNode event and memory no longer is allocated that drastically but it still is a bit. I think there might be reference to UnicodeString left even though it is emptied (reference count > 0).

How do I free node data in OnFreeNode event for UnicodeString in C++? I know that UnicodeString is allocated until all reference count is zero - so how do I force reference count to become zero?

Also, what if node is allocated in OnNodeInit - does the same applies in OnFreeNode event?

What if TVTNodeData structure is purely virtual - the node is never visible nor initialized neither using AddChild nor OnNodeInit, is then Finalize required, does the structure even exists in memory then?

Update: I discovered later that I was measuring memory usage incorrectly and that for strings setting them to empty string is really enough for clearing memory data. But - as Rob Kennedy suggests in his answer below, calling struct ~destructor is even better and equivalent to Finalize and also easier as it clears entire structure (if you have more strings in it).

Was it helpful?

Solution

Delphi's Finalize has the effect of freeing any compiler-managed types in a record. In C++, that's typically the job of a type's destructor. In your OnFreeNode event handler, call your data type's destructor directly:

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

That will call the destructor of the UnicodeString object, which will free the associated character data. When the tree control allocates the TVTNodeData for a node, it's in the same block of memory as the TVirtualNode object itself, so you can't just call delete.

The tree control initializes the data with all-bits-zero. If you have an object in your data for which that's not the correct initialization (which, to be formally correct, includes all non-POD types), then you should call the constructor of your data in the OnInitNode event. Use placement new to do that. For example:

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

That will call the constructors of the TVTNodeData members without allocating memory for an additional TVTNodeData instance.

If a node is never initialized, then it won't be finalized, either. The OnInitNode event will have never run, so the tree will know that the node hasn't been initialized. Uninitialized nodes don't get finalized, so you have nothing to worry about.

OTHER TIPS

I think not all of your nodes were validated, perhaps because they weren't shown. Try calling ValidateNode after AddChild.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top