Stack avec des constructeurs & des destructeurs Réimplémentons la liste chaînée (dans Stack) avec des constructeurs et des destructeurs appelés avec new et delete. Voixi le fichier d'en-tête modifié : //: C06:Stack3.h // Avec constructeurs/destructeurs #ifndef STACK3_H #define STACK3_H class Stack { struct Link { void* data; Link* next; Link(void* dat, Link* nxt); ~Link(); }* head; public: Stack(); ~Stack(); void push(void* dat); void* peek(); void* pop(); }; #endif // STACK3_H ///:~ Il n'y a pas que Stack qui ait un constructeur et un destructeur, mais la struct Link également : //: C06:Stack3.cpp {O} // Constructeurs/destructeurs #include "Stack3.h" #include "../require.h" using namespace std; Stack::Link::Link(void* dat, Link* nxt) { data = dat; next = nxt; } Stack::Link::~Link() { } Stack::Stack() { head = 0; } void Stack::push(void* dat) { head = new Link(dat,head); } void* Stack::peek() { require(head != 0, "Stack vide"); return head->data; } void* Stack::pop() { if(head == 0) return 0; void* result = head->data; Link* oldHead = head; head = head->next; delete oldHead; return result; } Stack::~Stack() { require(head == 0, "Stack non vide"); } ///:~ Le constructeur Link::Link( ) initialise simplement les pointeurs data et next, ainsi dans Stack::push( ) la ligne head = new Link(dat,head); ne fait pas qu'allouer un nouveau Link (en utilisant la création dynamique d'objet avec le mot-clé new, introduit au Chapitre 4), mais initialise également de façon ordonnée les pointeurs pour ce Link. Vous pouvez vous demander pourquoi le destructeur de Link ne fait rien – en particulier, pourquoi il ne supprime pas le pointeur data ? Il y a deux problèmes. Au chapitre 4, où la Stack a été présentée, on a précisé que vous ne pouvez pas correctement supprimer un pointeur void s'il pointe un objet (une affirmation qui sera prouvée au Chapitre 13). En outre, si le destructeur de Link supprimait le pointeur data, pop( ) se terminerait en retournant un pointeur sur un objet supprimé, ce qui serait certainement un bogue. Ceci désigné parfois par la question de propriété : Link, et par là Stack, utilise seulement les pointeurs, mais n'est pas responsable de leur libération. Ceci signifie que vous devez faire très attention de savoir qui est responsable. Par exemple, si vous ne dépilez pas et supprimez tous les pointeurs de la Stack, ils ne seront pas libérés automatiquement par le destructeur de Stack. Ceci peut être une question sensible et mène aux fuites de mémoire, ainsi savoir qui est responsable la destruction d'un objet peut faire la différence entre un programme réussi et un bogué – C'est pourquoi Stack::~Stack( ) affiche un message d'erreur si l'objet Stack n'est pas vide lors de la destruction. Puisque l'allocation et la désallocation des objets Link sont cachés dans la Stack – cela fait partie de l'implémentation interne – vous ne la voyez pas se produire dans le programme de test, bien que vous soyez responsable de supprimer les pointeurs qui arrivent de pop( ) : //: C06:Stack3Test.cpp //{L} Stack3 //{T} Stack3Test.cpp // Constructeurs/destructeurs #include "Stack3.h" #include "../require.h" #include <fstream> #include <iostream> #include <string> using namespace std; int main(int argc, char* argv[]) { requireArgs(argc, 1); // Le nom de fichier est un argument ifstream in(argv[1]); assure(in, argv[1]); Stack textlines; string line; // Lectrure du fichier et stockage des lignes dans la pile : while(getline(in, line)) textlines.push(new string(line)); // Dépiler les lignes de la pile et les afficher : string* s; while((s = (string*)textlines.pop()) != 0) { cout << *s << endl; delete s; } } ///:~ Dans ce cas-là, toutes les lignes dans les textlines sont dépilées et supprimées, mais si elles ne l'étaient pas, vous recevriez un message require( ) qui signifie qu'il y a une fuite de mémoire.