Éléments graphiques

XXIV.I.I. Gestion classique de la mémoire en C++

gtkmm autorise le programmeur à contrôler la durée de vie (c'est-à-dire, la construction et la destruction) de tout élément graphique de la même manière que celle de n'importe quel objet C++. Cette polyvalence autorise, soit l'utilisation des opérateurs new et delete pour créer et détruire les objets de manière dynamique, soit l'utilisation de données membres de classes régulières (qui sont détruites automatiquement quand la classe est détruite), soit l'utilisation d'instances locales (qui sont détruites dès que l'instance est hors de portée). Certaines boîtes à outils GUI C++ n'offrent pas cette souplesse : elles restreignent le programmeur à un sous-ensemble des fonctionnalités de gestion mémoire du C++.

Voici quelques exemples de gestion traditionnelle de la mémoire en C++ :

XXIV.I.I.I. Éléments graphiques à portée de classe

Si un programmeur n'a pas besoin d'allocation dynamique de mémoire, il peut se servir d'éléments graphiques automatiques à portée de classe. Un des avantages des éléments graphiques automatiques à portée de classe est que la gestion de la mémoire est regroupée en un seul endroit. Le programmeur ne risque pas de fuites de mémoire en oubliant un delete sur un élément graphique.

Les principaux inconvénients proviennent du fait que l'utilisation d'éléments graphiques à portée de classe sont en rapport avec l'implémentation de la classe plus que de son interfaçage dans l'en-tête de classe. Les éléments graphiques à portée de classe sont créés automatiquement et souffrent des mêmes inconvénients que n'importe quelle autre variable automatique à portée de classe.

#include <gtkmm/button.h>
class Foo
{
private:
  Gtk::Button theButton;
  // sera détruit en même temps que l'objet Foo
};

XXIV.I.I.II. Éléments graphiques à portée de fonction

Si un programmeur ne souhaite pas utiliser d'élément graphique à portée de classe, il peut aussi se servir d'éléments graphiques à portée de fonction. Les avantages de la portée de fonction par rapport à la portée de classe sont un masquage accru des données et une réduction des dépendances.

{
  Gtk::Button aButton;
  aButton.show();
  ...
  kit.run();
}

XXIV.I.I.III. Allocation dynamique avec new et delete

Même si, dans la plupart des cas, le programmeur préfére allouer des conteneurs pour détruire automatiquement leur contenu avec la fonction manage() (voir ci-dessous), l'utilisation de cette fonction n'est pas obligatoire. Les opérateurs traditionnels new et delete peuvent également être utilisés.

Gtk::Button* pButton = new Gtk::Button("Test");

// faire quelque chose d'utile avec pButton

delete pButton;
Ici, le programmeur détruit pButton pour prévenir une fuite de mémoire.

XXIV.I.II. Éléments graphiques gérés

Sinon, vous pouvez confier au conteneur d'élément graphique le contrôle du moment où il sera détruit. Dans la plupart des cas, vous voulez qu'un élément graphique dure aussi longtemps que son conteneur. Pour déléguer la gestion de la durée de vie d'un élément graphique à son conteneur, créez-le avec manage() et empaquetez-le dans son conteneur avec add(). Ainsi, l'élément graphique sera détruit au moment où son conteneur sera détruit.

XXIV.I.II.I. Allocation dynamique avec manage() et add()

gtkmm met à votre disposition les fonctions manage() et add() pour créer et détruire les éléments graphiques. Tout élément graphique, excepté la fenêtre principale, doit être ajouté ou empaqueté dans un conteneur pour être affiché. La fonction manage() marque l'élément graphique empaqueté de sorte que, lorsqu'il est ajouté dans un conteneur, ce conteneur devient responsable de sa destruction.

MyWidget::MyWidget()
{
  Gtk::Button* pButton = manage(new Gtk::Button("Test"));
  add(*pButton); //ajoute aButton à MyWidget
}
Ainsi, quand des objets du type MyWidget sont détruits, le bouton est également détruit. Il n'est plus nécessaire à l'avenir de détruire pButton pour libérer la mémoire occupée par le bouton ; cette opération a été déléguée à l'objet MyWidget.

gtkmm fournit également la fonction membre set_manage() pour tous les éléments graphiques. Il est possible de l'utiliser pour obtenir le même résultat qu'avec manage(), mais c'est plus délicat :

foo.add( (w=new Gtk::Label("Hello"), w->set_manage(), &w) );

est équivalent à

foo.add( manage(new Gtk::Label("Hello")) );

Bien entendu, le conteneur de haut niveau n'est pas intégré dans un autre conteneur. Le programmeur est responsable de la destruction du conteneur de haut niveau en utilisant les techniques habituelles du C++. Ainsi, votre fenêtre principale ne peut être qu'une instance dans votre fonction main().