POO - TP1 C++

mar. 05 avril 2005

Du C au C++

Vous allez, dans ce premier TD-TP, (re)découvrir les notions fondamentales que sont : la gestion de la mémoire, la visibilité et la durée de vie des objets, la modularité et la gestion de projets. De plus, les notions qui distinguent C++ de C, sans pour autant parler de programmation orientée objet, seront étudiées telles que : constantes, référence, valeurs de paramètres par défaut, fonctions surdéfinies, flux d'entrée-sortie.

Exercice 1 : référence, modularité.

Soit a une variable quelconque, la valeur de &a correspond à l'adresse mémoire de la variable a. L'élément syntaxique & associé à un type introduit quant à lui la notion de référence.

  1. Ecrire une procédure qui prend en paramètres deux références à des entiers et qui échange leur valeur :

    void Permuter(int&, int&);
    
  2. Encapsuler cette première procédure dans un module nommé "echangiste.cc". Ecrire le fichier à en-têtes correspondant. Ecrire dans un troisième fichier la fonction qui testera votre procédure et qui doit aussi constituer le point d'entrée de votre programme.

  3. Créer votre premier makefile, celui-ci devant gérer le source du module echangiste.cc, son fichier à en-têtes correspondant, le source de votre programme principal ainsi que leurs inter-dépendances.

  4. Ajouter le prototype de fonction suivant dans echangiste.h :

    void Permuter(Complexe&, Complexe&);
    

    Utiliser votre makefile : comment doit-il réagir ? Quelle règle en déduire (si ce n'est déjà fait) ?

Exercice 2 : structure, définition de type, surdéfinition de fonctions.

  1. Construire dans un fichier indépendant, nommé complexe.h, la structure Complexe, contenant les attributs a (partie réel) et b (partie imaginaire) tous deux de type float. En faire un type. Ecrire la procédure :

    void AfficherComplexe(const Complexe&);
    

    Quel(s) rôle(s) joue(nt) l'attribut const et l'opérateur & ? Aurait-on pu s'en passer (justifier la réponse) ? Où doit-on inclure le fichier d'en-têtes iostream ?

  2. Implanter la fonction :

    void Permuter(Complexe&, Complexe&);
    

    en utilisant exclusivement des variables de type Complexe.

  3. Tester la surdéfinition de la procédure Permuter de l'exercice 1 ainsi que la procédure AfficherComplexe dans votre programme principal.

Exercice 3 : le module "Complexe"

  1. Implanter la fonction somme de deux complexes :

    Complexe Somme(const Complexe&, const Complexe&);
    

    Rappel : soit z1 = a1 + i.b1 et z2 = a2 + i.b2, z1 + z2 = (a1 + a2) + i (b1 + b2)

  2. Implanter la fonction produit de deux complexes :

    Complexe Produit(const Complexe&, const Complexe&);
    

    Rappel : soit z1 = a1 + i.b1 et z2 = a2 + i.b2, z1 . z2 = (a1.a2 - b1.b2) + i (b1.a2 + a1.b2)

  3. Implanter la fonction module d'un complexe :

    float Module(const Complexe&);
    

    Rappel : soit z = a + i.b, |z| = sqrt(a² + b²)

  4. Implanter le conjugué d'un complexe :

    Complexe Conjuge(const Complexe&);
    

    Rappel : soit z = a + i.b, z = a - i.b (z désigne le conjugué de z)

  5. Tester toutes ces fonctions dans votre programme principal. En particulier, vérifier que Z . Z = |Z|²

Exercice 4 : variation sur le thème des complexes - initialisation, gestion de la mémoire

  1. Modifier la structure qui définit les complexes en lui ajoutant un champ "ident" (pour identificateur) de type entier non signé, qui doit jouer le rôle d'identificateur unique pour chaque complexe. Comment optimiser le coût mémoire de cette fonctionnalité ? Implanter la procédure void Init(complexe&); qui initialise le champ ident à un entier unique et les champs a et b à 0. Cette fonction sera appelée systématiquement après la déclaration d'un complexe. Remarque : il est bien entendu que cette stratégie n'empêche pas l'existence de deux complexes ayant le même identificateur (par l'affectation par exemple) mais sachez que C++ est en mesure de résoudre ce cas particulier.
  2. Visualiser à l'écran l'adresse d'une variable statique de type Complexe ainsi que celles de ses champs. Déclarer une référence à cette variable et faire de même avec cette référence. Créer une fonction Complexe Bidon(Complexe&); et faire de même avec le paramètre formel et la variable qui réceptionne la valeur retournée par la fonction. Conforter ainsi vos connaissances.
  3. Créer une fonction void CreerComplexe(Complexe**); qui alloue l'espace mémoire correspondant au pointeur de Complexe passé en paramètre et initialise correctement chacun de ses champs. Transformer cette fonction en void CreerComplexe(ptComplexe&); avec le type typedef Complexe* ptComplexe;
  4. Réécrire la fonction CreerComplexe en Complexe* CreerComplexe();. Ecrire la fonction Complexe* CreerVecteurComplexes(unsigned int); qui alloue dynamiquement un vecteur de Complexe. Afficher chacune des adresses des composants de ce vecteur.