C++ - TP1

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.

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 \(z_1 = a_1 + i\ b_1\) et \(z_2 = a_2 + i\ b_2\), alors :

    \[z_1 + z_2 = (a_1 + a_2) - i\ (b_1 + b_2)\]

  2. Implanter la fonction produit de deux complexes :

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

    Rappel : soit \(z_1 = a_1 + i\ b_1\) et \(z_2 = a_2 + i\ b_2\), alors :

    \[z_1 . z_2 = (a_1.a_2 - b_1.b_2) + i\ (b_1.a_2 + a_1.b_2)\]

  3. Implanter la fonction module d'un complexe :

    float Module(const Complexe&);

    Rappel : soit \(z = a + i\ b\), alors :

    \[\|z\| = \sqrt{a^2 + b^2}\]

  4. Implanter le conjugué d'un complexe :

    Complexe Conjuge(const Complexe&);

    Rappel : soit \(z = a + i\ b\), alors :

    \[\bar{z} = a - i\ b\]

  5. Tester toutes ces fonctions dans votre programme principal. En particulier, vérifier que \(z . \bar{z} = \|z\|^2\).

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();.

    Écrire la fonction Complexe* CreerVecteurComplexes(unsigned int); qui alloue dynamiquement un vecteur de Complexe.

    Afficher chacune des adresses des composants de ce vecteur.