C++ : TP 7 - Pointeurs et références

© D. Mathieu     mathieu@romarin.univ-aix.fr
I.U.T.d'Aix en Provence - Département Informatique
Créé le 13/01/2000 - Dernière mise à jour : 28/01/2001

Remarques préliminaires :

Sommaire

Opérateurs new et delete  exo_01
Tri par comptage  exo_02
Constructeur par copie  exo_03
Surcharges de l'injecteur et de l'affectation  exo_04


Opérateurs new et delete

exo_01

    Recopier les fichiers dirclass/CDuree.h, dirclass/CDuree.cxx et dirclass/Makefile dans les fichiers CDuree_01.h, CDuree_01.cxx et Makefile_01. Ajouter à la classe CDuree le modifieur SetDuree() permettant d'initialiser les données membres. Prévoir une exception en cas de valeur négative du paramètre. Il est alors possible de simplifier encore le constructeur en le limitant à un appel à SetDuree().

    Dans la fonction Exo_01(), définir un objet Duree de la classe CDuree, l'initialiser avec une valeur lue au clavier, et l'afficher. Afin d'alléger cette opération, modifier la fonction membre Afficher() écrite précédemment pour obtenir par exemple l'affichage suivant :
 
[   14:06:56:07]

en utilisant des zones de longueur fixe (par exemple 5 pour les jours et 2 pour les autres valeurs), comme fait précédemment, grâce au manipulateur setw(), et en utilisant le manipulateur setfill ('0'), qui utilise le caractère qui lui est passé en paramètre comme caractère de remplissage (ici les zéros non significatifs des zones numériques). Contrairement à setw() dont l'action est limitée à l'affichage qui suit immédiatement, l'action de setfill() se prolonge jusqu'à ce qu'un nouveau caractère de remplissage soit explicitement indiqué. Donc, en fin d'édition de la durée, ne pas oublier de restaurer le caractère de remplissage par défaut (l'espace).

    Modifier le fichier Makefile_01, compiler et tester;

    Lorsque le contrôle du programme sort de la portée de l'objet Duree (en fin de fonction Exo_01()), le destructeur par défaut est automatiquement appelé. Afin de mettre ce mécanisme en évidence, ajouter un destructeur à la classe CDuree, de profil :
 
~CDuree (void) throw ();

qui affiche l'objet en indiquant qu'il est détruit :
 
[   14:06:56:07] détruit

    Compiler et tester.

    A la fin de la fonction Exo_01(), déclarer un pointeur de CDuree, créer l'objet dynamiquement (opérateur new), puis l'initialiser à partir d'une nouvelle valeur lue au clavier. Afin de montrer que le constructeur par défaut est appelé par l'opérateur new, ajouter une édition de l'objet créé en fin du constructeur.

    Compiler et tester. Vérifier que le destructeur de l'objet dynamique n'a pas été appelé.

    Faire le nécessaire pour que l'objet dynamique soit détruit lorsque son pointeur n'est plus accessible (delete). Compiler et tester.

    Lorsqu'un tableau est créé, le constructeur par défaut est appelé pour chacun des objets. Inversement, lorsqu'il est détruit, c'est le destructeur de chaque objet qui est appelé dans l'ordre inverse de la création. Vérifier sur deux petits tableaux (de 3 durées par exemple) déclarés l'un en variable automatique (sur la pile d'exécution) l'autre en mémoire dynamique. Initialiser chacun de ces vecteurs par une petite boucle. Compiler et tester.

Corrigés : exo_01.cxx      -      CDuree_01.h      -      CDuree_01.cxx      -      Makefile_01

Sommaire


Tri par comptage

exo_02

    Recopier les fichiers CDuree_01.h, CDuree_01.cxx et Makefile_01 dans les fichiers CDuree_02.h, CDuree_02.cxx et Makefile_02. Supprmier les affichages dans le constructeur et le destructeur de CDuree.

    Lorsque des objets occupent des espaces de mémoire importants (des structures complexes, des images, etc...), et qu'ils doivent être fréquemment déplacés ou permutés (par exemple dans le tri à bulles vu précédemment), il est souhaitable de les stocker une fois pour toutes en mémoire (dynamique ou automatique) lors de leur création et d'y accéder par l'intermédiaire de pointeurs (ou d'itérateurs). La permutation d'un objet revient alors à celle de son pointeur, qui n'occupe, sur les machines courantes, que 4 ou 8 octets.

    De plus, nous avons vu que l'utilisation de la classe conteneur générique vector<> permet d'allonger cette structure au cours de son utilisation (push_back()), mais que cette opération nécessite souvent une réallocation au fur et à mesure de l'accroissement de la structure. En revanche, la réservation d'espace mémoire en début de programme peut être coûteuse si elle est incomplètement utilisée. L'utilisation des pointeurs est là aussi une alternative intéressante.

    Pour plus de commodités par la suite, créer un type CVDuree : "vecteur de pointeurs de CDuree" et un type CIter "itérateur sur ce vecteur". Dans la fonction Exo_02(), créer un vecteur VDuree de type CVDuree, d'une taille suffisante (par exemple 100 éléments). Au clavier, dans une boucle jusqu'à fin-de-fichier, lire des durées (4 ou 5), créer dynamiquement les objets CDuree correspondants, dont les adresses seront stockées dans le vecteur (en respectant l'ordre chronologique). En fin de saisie, redimensionner le vecteur VDuree à sa taille réelle.

    En utilisant un itérateur, afficher les durées stockées dans l'ordre chronologique. Modifier le fichier Makefile_02, compiler et tester.

    L'étape suivante nécessite l'ajout de l'opérateur < à la classe CDuree, de profil :
 
bool operator < (const CDuree & Duree) throw ();

qui renvoie true lorsque la donnée membre m_Duree de l'objet considéré est inférieure à celle de l'objet passé en paramètre.

    En utilisant la méthode du tri par comptage, créer un second vecteur VDureeTrie de même taille et de même type que VDuree, dans lequel seront rangés les pointeurs vers les durées dans l'ordre croissant (figure 1). Le tableau d'entiers nécessaire au comptage devra être détruit dès que le réarrangement aura été effectué.


figure 1 : tri par comptage

    Vérifer que le vecteur est toujours accessible selon les deux ordres : chronologique et par durée croissante.

    Avez-vous bien récupéré tout l'espace mémoire alloué ?

Corrigés : exo_02.cxx      -      CDuree_02.h      -      CDuree_02.cxx      -      Makefile_02

Sommaire


Constructeur par copie

exo_03

    Recopier les fichiers CDuree_02.h, CDuree_02.cxx et Makefile_02 dans les fichiers CDuree_03.h, CDuree_03.cxx et Makefile_03.

    Le constructeur par copie est une des quatre fonctions spéciales fournies par défaut par le compilateur C++, en même temps que le constructeur par défaut, le destructeur et l'affectation. Il est parfois utilisé explicitement, mais il est souvent appelé directement par le compilateur, à l'insu du développeur. L'objectif de cet exercice est de mettre en évidence ces appels cachés.

    A la classe CDuree, ajouter le constructeur par copie, de profil :
 
CDuree (const CDuree & Duree) throw ();

qui se contente d'initialiser les différentes données membres du nouvel objet avec les donées membres de l'objet passé en paramètre. Il n'a aucune raison de lever une exception.

    Le temps de mettre en évidence les appels à ce constructeur, ajouter en dernière instruction  l'affichage de l'objet créé. Supprimer l'affichage précédemment placé dans le premier constructeur.

    A l'espace des noms anonyme, ajouter la fonction Clone(), de profil suivant :
 
CDuree Clone (CDuree Duree);

qui affiche la durée passée en paramètre au moyen de sa propre fonction Afficher(), la modifie puis la renvoie comme valeur de retour.

    Dans la fonction Exo_03(), créer et initialiser un objet de la classe CDuree, le dupliquer au moyen du constructeur par copie utilisé explicitement, puis le passer en paramètre effectif de la fonction Clone(). Le récupérer dans un objet CDuree différent.

    Modifier le fichier Makefile_03. Tester et identifier les affichages correspondant aux appels aux différents constructeurs et destructeurs.

Corrigés : exo_03.cxx      -      CDuree_03.h      -      CDuree_03.cxx      -      Makefile_03

Sommaire


Surcharges de l'injecteur et de l'affectation

exo_04

    Recopier les fichiers CDuree_03.h, CDuree_03.cxx et Makefile_03 dans les fichiers CDuree.h, CDuree.cxx et Makefile. Supprimer l'affichage dans le constructeur par copie.

        Afin d'alléger l'écriture, ajouter à la classe CDuree la surcharge de l'opérateur <<. Désormais, la fonction Afficher() n'est conservée que pour assurer la compatibilité ascendante. Elle peut d'ailleurs être réécrite en utilisant l'opérateur <<.

    L'affectation est la dernière des quatre fonctions spéciales. Bien que la version par défaut, utilisée dans l'exercice précédent, soit satisfaisante, il est important de savoir la surcharger, ce qui est demandé ici. Ne pas oublier de tester l'auto-affectation (self-assignment), c'est-à-dire l'affectation d'un objet à lui-même, qui doit se solder par une opération nulle.

    Plusieurs fonctions sont suffisamment courtes pour être mises en "inline". Recopier le fichier CDuree.cxx dans CDuree.hxx. Supprimer dans chacun des deux les fonctions laissées dans l'autre. Effectuer les quelques modifications nécessaires (suppressions des fichiers inclus inutiles, suppression de "using namespace std" dans CDuree.hxx et préfixage correspondant, etc.). Inclure le fichier CDuree.hxx dans CDuree.h.

    Dans la fonction Exo_04(), tester plusieurs exemples, en particulier des affectations multiples (a = b = c = d)

    Modifier le fichier Makefile, compiler et tester.

Corrigés : exo_04.cxx      -      CDuree.h      -      CDuree.hxx      -      CDuree.cxx      -      Makefile

Sommaire


Téléchargement des corrigés :

tpptr.zip

Chemins d'accès aux sources des corrigés :

~mathieu/PARTAGE/src/tp/tpC++/tpptr/dirptr

© D. Mathieu     mathieu@romarin.univ-aix.fr
I.U.T.d'Aix en Provence - Département Informatique