C++ : TP 6 - Les classes

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

Remarques préliminaires :

Sommaire

Première classe  exo_01
Fichier de test  exo_02
Compilation séparée  exo_03
Constructeur par défaut  exo_04
Traitement des exceptions  exo_05


Première classe

exo_01

    La première partie de cet exercice consiste à préparer la fonction main() pour la rendre plus résistante aux éventuelles exceptions (voir TP 5). Selon la structure adoptée au département, la fonction main() a pour unique rôle d'appeler une autre fonction qui représente le véritable traitement à effectuer : Exo_??(). N'ayant indiqué aucune exception particulière dans le profil de ces fonctions :
 
void Exo_?? (void);

elles sont, par convention, susceptibles de lever n'importe quelle exception. Pour le moment, les seules exceptions envisagées sont :

    Modifier la fonction main() pour intercepter ces deux types d'exceptions. Dans chaque cas, afficher un message adapté, puis retourner au shell une valeur significative, par exemple en utilisant un type énumératif (voir identificateurs des constantes) dans lequel une constante vaudrait 0 (pas d'erreur), une autre 255 (valeur maximale possible : exception non identifiée) et une autre 254.

    L'objectif de cet exercice est de construire une classe permettant de gérer une durée de temps (un délai), exprimé soit en secondes, soit en jours, heures, minutes et secondes. Les normes de programmation devront être respectées : identificateurs des données membres, identificateurs des paramètres, terminaisons des classes.

    Dans l'espace de noms anonyme, déclarer la classe CDuree qui possède les données membres suivantes :

et les fonctions membres suivantes :     Les définitions (corps) de toutes les fonctions membres devront être faites en dehors de la déclaration de la classe. Attention à utiliser à bon escient le qualificatif const.

    Dans la fonction Exo_01(), construire différentes durées, puis tester les fonctions Incr() et Decr() pour tous les cas intéressants possibles. Afficher chacune des durées générées.

    Renommer le fichier Makefile en Makefile_01. Compiler [1] et tester.

Corrigés : exo_01.cxx

Sommaire


Fichier de test

exo_02

    Il est lourd de coder "en dur" tous les cas possibles dans le programme de test, et toute modification du jeu d'essais nécessite une nouvelle compilation. Il est aussi inutile de rendre le programme "conversationnel" pour tester au clavier les différents cas. L'expérience montre que la couverture de tests est très incomplète, et surtout qu'après une modification du code, les tests ne sont pas refaits entièrement. La seule solution acceptable est le fichier de test. C'est cette dernière solution que nous allons illustrer, seulement pour le test des constructeurs.

    Créer un fichier texte FichDurees, contenant un certain nombre de durées entières.

    Recopier le fichiers exo_01.cxx dans exo_02.cxx. Dans la fonction Exo_02(), créer un type "vecteur de durées", et un vecteur de ce type. Dans une boucle, lire chaque durée sur le fichier et remplir le vecteur (en utilisant la fonction push_back()). Après la dernière lecture, balayer le vecteur et afficher chacune des durées dans le format indiqué ci-dessus.

    Compiler et tester (en utilisant le même fichier Makefile_01 que précédemment).

Corrigés : exo_02.cxx      -      FichDurees

Sommaire


Compilation séparée

exo_03

    Si la classe CDuree est susceptible d'être utilisée dans plus d'un seul fichier, ce qui est déjà le cas, elle doit en être extraite et mise à la disposition de tout utilisateur. De ce fait elle ne peut plus appartenir à un espace de noms anonyme. Comme la norme C++ préconise de ne pas utiliser d'identificateurs globaux, il faut donc placer la classe CDuree (déclaration et définition) dans un espace de noms nommé. Pour le moment, nous nommerons cet espace nsWorking.

    De plus, la déclaration de la classe sera placée dans un fichier CDuree.h susceptible d'être inclus dans tout programme utilisant cette classe. Les définitions des méthodes seront au contraire regroupées dans un fichier CDuree.cxx, et compilées une fois pour toutes dans un fichier objet CDuree.o. Ce fichier devra ensuite être utilisé lors de la phase d'édition de liens.

    Recopier le fichier exo_02.cxx dans les fichiers CDuree_03.h, CDuree_03.cxx et exo_03.cxx.

    Dans le fichier CDuree_03.h, remplacer l'espace de noms anonyme par l'espace de noms nsWorking. Extraire tout ce qui ne concerne pas la déclaration de la classe CDuree (voir définitions et déclarations des classes). Ajouter les directives d'inclusion conditionnelle.

    Dans le fichier CDuree_03.cxx, remplacer l'espace de noms anonyme par l'espace de noms nsWorking. Inclure le fichier CDuree_03.h. Extraire tout ce qui ne concerne pas la définition de la classe CDuree.

    Du fichier exo_03.cxx, supprimer tout ce qui concerne la classe CDuree et le remplacer par l'inclusion du fichier CDuree_03.h.

    Recopier le fichier Makefile_01 dans Makefile_03. Modifier le fichier Makefile_03, pour permettre de compiler séparément les deux fichiers .cxx et effectuer l'édition de liens.

    Compiler et tester.

    La commande Unix touch permet, dans une de ses options les plus simples, de modifier la date d'un fichier sans pour autant modifier le fichier correspondant. Après avoir effectué une compilation complète et réussie de l'ensemble, modifier la date de l'un quelconque des fichiers (par exemple CDuree_03.h) :
 
duo>touch CDuree_03.h

et verifier quels sont les fichiers qui sont recompilés en relançant la commande make.

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

Sommaire


Constructeur par défaut

exo_04

    Recopier les fichier exo_03.cxx, CDuree_03.h, CDuree_03.cxx et Makefile_03 dans exo_04.cxx, CDuree_04.h, CDuree_04.cxx et Makefile_04. Dans la fonction Exo_04(), et afin de ne pas obliger le programme à redimensionner le vecteur de durées à chaque insertion en queue, donner au vecteur une taille suffisante (par manque de temps, on ne vérifiera pas en cours d'exécution), puis le remplir en lisant les indications du fichier.

    Modifier le fichier Makefile_04.

    Compiler et tester. Vous constatez une erreur de compilation. Les raisons sont expliquées dans "Constructeurs de la classe vector" et dans la "Fonction resize() de la classe vector".

    Ajouter des valeurs par défaut aux paramètres du constructeur et des fonctions Incr() et Decr(). Compiler et tester.

Corrigés : exo_04.cxx      -      CDuree_04.h,      -      CDuree_04.cxx,      -      Makefile_04

Sommaire


Traitement des exceptions

exo_05

    Dans la version précédente de la fonction Decr(), nous avons remplacé une éventuelle durée négative par une valeur nulle. L'utilisateur n'en est pas informé et n'a aucune chance de s'en apercevoir. Une solution possible dans ce cas serait d'utiliser pour cette fonction une valeur de retour booléenne indiquant si l'opération s'est bien déroulée. La solution idéale est cependant d'utiliser les exceptions puisqu'il doit s'agir d'un événement exceptionnel. Le but de cet exercice est mettre en place un mécanisme permettant de sécuriser au maximum les opérations actuelles sur les durées, et autorisant une éventuelle extension ultérieure.

    Une première exception devrait être levée lorsque, dans la fonction Decr(Delta), la valeur de Delta de la fonction est supérieure à m_Duree.

    Il y a lieu de revenir sur le constructeur. Le type unsigned long int empêche de passer une constante négative comme paramètre effectif, le compilateur pouvant détecter cette erreur. En revanche, si le paramètre effectif est une variable de type int négative, le compilateur ne peut anticiper l'erreur. A l'exécution, la valeur reçue en paramètre est interprétée comme un nombre positif (en complément à 2) et le résultat est faux mais non signalé. Il est donc préférable d'utiliser un type entier signé pour le paramètre formel, et de vérifier a posteriori si la valeur passée est bien positive ou nulle. Dans le cas contraire, une exception doit être levée.

    Recopier les fichier exo_04.cxx, CDuree_04.h, CDuree_04.cxx et Makefile_04 dans exo_05.cxx, CDuree.h, CDuree.cxx et Makefile.

    Dans la partie publique de la classe CDuree, ajouter le type énumératif CodErr_t, représentant le type d'erreur. Les constantes de cette énumération peuvent être CstNoError, de valeur nulle, CstErrDecr, et CstErrConstr levée lorsque la valeur passée au constructeur est nulle.

    Dans la partie publique de la classe CDuree, ajouter la classe CException, dérivée de la classe standard exception, possédant deux données membres :

et les fonctions membres suivantes :     Modifier le profil de chacune des fonctions de la classe CDuree pour indiquer si elles lèvent des exceptions, et si oui, lesquelles.

    Modifier le type du paramètre du constructeur en un type signé (suite à la remarque ci-dessus).

    Modifier le constructeur et la fonction Decr() pour lever des exceptions en cas d'erreur. Puisque, à cause de la levée d'exception, la liste d'initialisation du constructeur ne peut plus être utilisée, et que les initialisations des différentes données membres doivent être effectuées dans le corps du constructeur, il est plus élégant d'utiliser la fonction Normaliser().

    Dans la fonction Exo_05(), construire deux blocs try-catch. Dans le premier, construire une durée avec une valeur négative. Capturer l'exception et afficher les informations disponibles la concernant. Dans le second, construire une durée positive, et décrémenter avec une valeur de Delta trop grande. Capturer l'exception, afficher un message différent en fonction du code d'erreur récupéré (schéma de choix), puis relever l'exception. Que se passe-t-il au niveau de la fonction main() ?

Corrigés : exo_05.cxx      -      CDuree.h,      -      CDuree.cxx,      -      Makefile

Sommaire


Téléchargement des corrigés :

tpclass.zip

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

~mathieu/PARTAGE/src/tp/tpC++/tpclass/dirclass


[1] Lorsque le fichier que doit utiliser la commande make n'est aucun des deux fichiers standard makefile ou Makefile, le nom du fichier doit être indiqué à la commande :

      make -f Makefile_01 nom=exo_01

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