Le but de ces travaux pratiques est de créer un logiciel permettant de "gérer" l'ensemble des personnes étudiant ou travaillant à Télécom Paris. Ce logiciel sera réalisé par étapes, en començant par la conception et l'implémentation des classes principales, puis en rajoutant diverses fonctionnalités.
Etape préliminaire. Arbre d'héritage
Concevoir l'arbre d'héritage des classes principales sur papier en spécifiant les principales méthodes, données (et types de données) qui seront utilisées par ces classes.
On tiendra compte des remarques suivantes: il y a 2 grandes catégories de personnes présentes à l'Ecole : les étudiants et les personnels qui se répartissent en:
-diverses catégories d'étudiants : les élèves, les thésards, les stagaires, (les éleves pouvant encore être subdivisés en catégories plus spécifiques)
--diverses catégories de personnels : enseignants, admistratifs, techniques, etc.
il serait souhaitable de définir une classe racine factorisant les caractéristiques communes à toutes les personnes présentes
les caractéristiques typiques incluent :
pour tous : état civil et coordonnées
pour les étudiants: cursus suivi, longueur du cursus, année courante (ainsi que les UEs et les notes pour les élèves)
pour les personnels: bureau, fonction, etc.
les fonctionnalités principales incluent:
la création, la destruction, la modification, la copie d'un élément
les entrées-sorties interactives (initialisation au clavier ou affichage à l'écran d'un objet)
les entrées-sorties sur disque (sauvegarde d'un objet / récupération d'un objet) d'un objet)
et d'autres fonctionnalites que l'on considèrera ultérieurement ...
IMPORTANT : on se limitera ici à l'essentiel sans trop entrer dans les détails l'important étant de déterminer ce qui peut être factorisé pour faciliter le choix des classes et les relations d'héritage.
1e étape. Déclaration et implémentation de la classe Racine
a) Ecrire le header C++ (déclaration) de la classe Racine (on pourra trouver un nom plus adapté) de l'arbre d'héritage des classes. Cette classe se limitera à la gestion des coordonnées principales d'une personne : nom, prénom, age, email. Déclarer les méthodes adéquates pour créer un objet de cette classe, modifier ou accèder à ses champs et afficher le contenu global de l'objet à l'écran (avec les intitulés correspondants).
Remarques: on utilisera les chaînes de caractères du C++ (classe string). se limiter à la gestion des champs précédemment indiqués (nom, prenom, age, email) pour éviter de passer trop de temps sur cette étape.
b) Implementer cette classe (dans un fichier .cc). Le compiler avec g++ et corriger les (éventuelles !) erreurs de syntaxe.
c) Ecrire un petit programme de test qui crée un objet et permette d'initialiser ou modifier son contenu et de l'afficher à l'écran.
2e étape. Déclaration et implémentation de la classe Etudiant
a) Ecrire le header correspondant (utiliser des fichiers disctincts pour les diverses classes).
On prendra en compte: le nom du cursus (un seul nom pour simplifier) ainsi que l'année actuelle dans le cursus.
b) Ecrire et compiler le fichier d'implémentation correspondant puis tester les deux classes déjà implémentées. Pour ce faire, écrire un Makefile puis utiliser la commande make (on pourra s'inspirer de cet exemple de Makefile ; attention ne pas faire de coupé-collé (à cause des tabulations) mais sauvegarder via les commandes du navigateur).
3e étape. Déclaration et implémentation de la classe Employé
Même principe que précédemment, les caractéristiques d'un employé étant le nom de son département et son numero de bureau.
4e étape. Gestion générique
On veut maintenant pouvoir gérer géneriquement une liste d'étudiants et d'employés. On commencera pour ce faire par utiliser un tableau de pointeurs que l'on initialisera de manière appropriée dans un programme de test. Ecrire une fonction qui permette d'afficher le contenu complet de tous les éléments contenus dans la liste, quel que soit leur type (les informations disponibles n'étant pas les mêmes pour un étudiant ou un employé). Cette fonction devra respecter le principe d'encapsulation (pas d'accès direct aux champs des objets par une fonction non-membre) et on évitera toute duplication inutile de code.
Comment s'appelle la propriété, caractéristique de l'OO que nous devrons employer ? Comment les méthodes doivent-elles être déclarées pour que cette propriété soit effective en C++ ?
Remarque : la gestion générique de la liste devra apparaître dans votre programme final (éventuellement sous une forme modifiée). Ne supprimez pas le code correspondant en faisant les questions suivantes !
5e étape. Déclaration et implémentation de la classe Eleve
On veut maintenant spécialiser la classe Etudiant en définissant la sous-classe Eleve. Une caractérisque des élèves est qu'ils suivent des UEs qui sont sanctionnées par des notes. Faire en sorte que l'on puisse associer une liste de notes à un élève (seulement les notes, pour simplifier on ne tiendra pas compte des noms des UEs). Ces notes seront stockées dans l'"Eleve" en utilisant un tableau (pas un vecteur de la STL). On définira:
un constructeur qui permettra de spécifier les notes à l'initialisation
une méthode qui permettra de changer les notes a posteriori
Remarques: dans les 2 cas on se contentera (pour simplifier) de changer toutes les notes d'un coup en passant un tableau en argument
le nombre de notes n'est pas connu à l'avance par la classe Eleve (i.e. ça ne doit pas etre une constante de cette classe)
dans tous les cas, pensez à initialiser correctement le tableau de notes, et ce en utilisant les opérateurs C++ appropriés.
Que faut-il faire pour éviter les problèmes de gestion mémoire et respecter les règles de l'encapsulation ?
Les autres sous-catégories d'Etudiants (pas déclaréees pour l'instant) peuvent aussi avoir des notes. Mais celles-ci ne sont pas calculées de la même manière (ils ne suivent pas forcement des UEs, etc.).
Rajouter à la classe Etudiant une méthode qui retourne la note globale. Son calcul étant indéfini au niveau de cette classe, utiliser une méthode abstraite. Qu'est-il alors nécessaire de faire dans les sous-classes de Etudiant ? Le faire dans le cas de la classe Eleve (en calculant la moyenne des notes du tableau). La méthode d'affichage devra également afficher la moyenne et les différentes notes du tableau.
6e étape. Destruction
Contrairement à Java, C++ ne propose pas en standard de "garbage collector" pour récupérer la mémoire dynamique précédemment allouée et qui n'est plus utilisée (= qui n'est plus référencée par aucun pointeur). Que faut-il faire pour détruire correctement les objets (sans fuite mémoire ni "segmentation fault" !). Rajoutez le code nécessaire et faites quelques tests mélangeant destruction et construction d'objets. Question: les classes comportant des méthodes virtuelles doivent, logiquement, avoir des destructeurs virtuels. Pourquoi ?
7e étape. Constance et références
(Attention : cette question est importante : elle comprend 2 parties qui doivent être faites en même temps pour gagner du temps)
a) Vérifiez que vous avez correctement implémenté les règles de constance dans les classes déja réalisées. Citez les 3 cas de figures ou le mot-clé const doit être utilisé. Modifiez votre code en conséquence partout où c'est nécessaire.
b) Vérifiez également que vous avez utilisé le passage par référence des arguments des méthodes chaque fois que cela était nécessaire.
Question: comment imposer qu'un objet créé avec new soit à valeur constante (par exemple un objet de classe Eleve dans votre fonction main()). Que se passerait'il si on essayait de lui appliquer la methode qui change les notes d'un élève ? Même question pour la méthode qui permet d'afficher son contenu. Pourquoi ?
8e étape. Initialisation et affectation
Vos objets contiennent probablement des pointeurs (typiquement vers le tableau de notes). Il est donc dangereux de les recopier par simple affectation (les variables d'instances des 2 objets pointeraient vers les mêmes valeurs).
Questions: le même problème peut également se poser à l'intialisation d'un objet : dans quel cas (donnez un exemple) ?
comment faire pour que l'affectation et l'initialisation soient interdites par le compilateur pour toutes les classes de la hiérarchie ?
comment faire pour que l'affectation et l'initialisation des objets de classe Eleve puissent se faire sans danger ? Rajoutez le code correspondant dans vos classes.
9e étape. STL
Réécrire l'étape 4 en utilisant les vecteurs puis les listes chaînées de la STL.
10e étape. Entrées/sorties et fichiers
Rajouter les fonctionnalités d'entrées-sorties manquantes : lecture au clavier des champs des classes et lecture / écriture sur un fichier. Pensez à bien modulariser votre code et à respecter les principes de l'orienté objet.
Note: on pourra éventuellement utiliser des ofstream et des ifstrems (voir le lien Manuel des librairies C++ de Sun ou ... le Web!)
Etape finale : Doxygen
Commenter votre code source (en particulier les headers) et générer la documentation en utilisant Doxygen (cf. aussi ce tutoriel)






