VirtualTreeView Finalize в C ++ Builder для UnicodeString

Я использую VirtualTreeView в C ++ Builder и использую его со следующей структурой:

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/ рекомендует использовать Finalize () в OnFreeNode событии. Хорошо до сих пор.

Но в C ++ нет Finalize (). Я попробовал pNode->Caption="" в событии OnFreeNode, и память больше не выделяется так резко, но это все еще немного. Я думаю, что ссылка на UnicodeString может остаться, даже если она пуста (счетчик ссылок> 0).

Как освободить данные узла в событии OnFreeNode для UnicodeString в C ++? Я знаю, что UnicodeString выделяется до тех пор, пока все счетчики ссылок не станут равными нулю - так как мне заставить счетчик ссылок стать равным нулю?

Кроме того, что, если узел выделен в OnNodeInit - применимо ли то же самое к событию OnFreeNode?

Что, если структура TVTNodeData является чисто виртуальной - узел никогда не виден и не инициализируется ни с помощью AddChild, ни с помощью OnNodeInit, тогда требуется финализация, тогда эта структура вообще существует в памяти?

Обновление: позже я обнаружил, что неправильно измерял использование памяти и что для строк достаточно установить пустую строку для очистки данных в памяти. Но - как предлагает Роб Кеннеди в своем ответе ниже, вызов struct ~ destructor даже лучше и эквивалентен Finalize, а также проще, поскольку он очищает всю структуру (если в ней больше строк).


person Coder12345    schedule 16.04.2012    source источник
comment
Вы указали правильный NodeDataSize? также попробуйте проверить каждый узел, который вы добавляете в дерево, также WideString вариант для заголовка ?.   -  person kobik    schedule 17.04.2012
comment
Да, я сделал VTree->NodeDataSize = sizeof(TVTNodeData); перед циклом. WideString возможен, но в чем преимущество его использования? Это не подсчет ссылок, поэтому установка пустой строки освобождает всю память? Разве UnicodeString не более дружественен к VCL?   -  person Coder12345    schedule 17.04.2012
comment
WideString относится к типу COM BSTR и управляется распределителем кучи COM Windows без подсчета ссылок. но простая установка UnicodeString на пустую строку должна быть равна Finalize.   -  person kobik    schedule 17.04.2012
comment
Спасибо, я обнаружил, что утечки все-таки нет. Но я обнаружил, что WideString потребляет меньше памяти, чем UnicodeString, вы можете это подтвердить?   -  person Coder12345    schedule 17.04.2012
comment
Да, WideString потребляет меньше памяти, чем UnicodeString. Widestring содержит только 4-байтовое целое число (длина строки) в дополнение к символьным данным, тогда как UnicodeString содержит два 4-байтовых целых числа (длина строки и счетчик ссылок) и два 2-байтовых целых числа (размер элемента каждого символа - всегда 2 - и кодовая страница строки - всегда CP_UTF16) в дополнение к символьным данным.   -  person Remy Lebeau    schedule 18.04.2012


Ответы (2)


Finalize Delphi освобождает любые типы, управляемые компилятором, в записи. В 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 никогда не запустится, поэтому дерево будет знать, что узел не был инициализирован. Неинициализированные узлы не финализируются, поэтому вам не о чем беспокоиться.

person Rob Kennedy    schedule 17.04.2012
comment
Спасибо, я также понял, что могу вызвать ~ TVTNodeData (), но я беспокоился, что это может привести к некоторому нарушению доступа позже (чего не было вначале). Я также теперь лучше понимаю механику VirtualTreeView. Спасибо за этот ответ, очень признателен. - person Coder12345; 17.04.2012

Я думаю, что не все ваши узлы были проверены, возможно, потому, что они не были показаны. Попробуйте вызвать ValidateNode после AddChild.

person Remko    schedule 17.04.2012
comment
Итак, вы говорите, что Finalize не требуется в программах на C ++, а установка UnicodeString на пустую строку очищает всю используемую память? - person Coder12345; 17.04.2012
comment
После еще нескольких тестов я обнаружил, что ValidateNode не имеет никакого значения. Утечки памяти вроде нет. Если узел не инициализирован, память не используется. Тем не менее, я обнаружил, что WideString потребляет меньше памяти, чем UnicodeString (учитывая тот факт, что в списке были тысячи записей - тысячи WideStrings / Unicodestrings), может ли кто-нибудь подтвердить, что это правда, что WideString потребляет меньше памяти? - person Coder12345; 17.04.2012