Gestion mémoire avec TreeView/TreeNode
Par Cédric Tabin le mercredi 18.03.2020, 17:00 - C/C++ - Lien permanent
Lorsque l'on a besoin de stocker des informations spécifique dans un composant graphique, la gestion de la mémoire peut vite de venir un casse-tête...
Nous utilisons un composant TCustomTreeView
qui gère une arborescence avec des TTreeNode
. Ce dernier permet le stockage d'un pointeur void* Data
, ce qui est très pratique. Mais voila, comment faire pour supprimer ce pointeur lorsque le TTreeNode
est supprimé ?
Le problème est que le code qui gère l'attribution à Data
se trouve dans une fonction générique, hors contexte:
void AddTreeNodeCharTag(TTreeNode* TreeNode, const UnicodeString& DataStr) { wchar_t* CHARTAG = new wchar_t[256]; wcscpy(CHARTAG, DataStr.w_str()); TreeNode->Data = (void*)CHARTAG; }
En l'état, c'est une magifique fuite mémoire ! En effet, le tableau CHARTAG
n'est jamais supprimé. De plus, la conversion wchar_t* <-> void*
n'est pas très plaisante...
Evidemment, créer un tableau dans une variable globale qui stockerait les instances de wchar_t*
à supprimer et qui serait nettoyé sporadiquement (et encore, comment savoir quels pointeurs supprimer ?) ressemble plus à de la bricole qu'autre chose. Le but est d'avoir une solution propre sans avoir à ajouter du code ailleurs pour la gestion mémoire de ces données. Et bien grâce à TComponent
, c'est possible !
class InternalTreeData : public TComponent { public: __fastcall virtual InternalTreeData(TComponent* owner); __fastcall virtual ~InternalTreeData(); TTreeNode* node; UnicodeString tag; }; __fastcall InternalTreeData::InternalTreeData(TComponent* owner) : TComponent(owner) {} __fastcall InternalTreeData::~InternalTreeData() { node = NULL; }
La class InternalTreeData
hérite de TComponent
et sera rattachée à owner
. Ainsi, lorsque le owner
sera supprimé, c'est lui-même qui, en interne, s'occupera de supprimer tous les autres composants dont il est le owner.
Et comme TTreeNode
n'est pas un TComponent
, il faut récupérer le TCustomTreeView
associé avec TreeNode->Owner->Owner
. Ainsi, lorsque l'écran sera fermé, le composant TreeView sera supprimé, ainsi que toutes les données associées.
void AddTreeNodeCharTag(TTreeNode* TreeNode, const UnicodeString& DataStr) { if (TreeNode == NULL) { return; } InternalTreeData* itd = new InternalTreeData(TreeNode->Owner->Owner); TreeNode->Data = itd; itd->node = TreeNode; itd->tag = DataStr; }
Evidemment, si les noeuds du TreeView sont supprimés/créés plusieurs fois (typiquement si l'arbre change), la mémoire va augmenter temporairement, puisque les noeuds eux-mêmes ne peuvent pas être les owners des InternalTreeData
.
A noter également que cette solution permet d'éviter de devoir faire des copies de tableaux et également de pouvoir nommer le type de données stockées, facilitant ainsi la lecture et la compréhension d'un autre développeur.