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.