T2.poo : Sujet du TP4 T2.poo : Sujet du TP4
Groupe ESIEE, Denis BUREAU, décembre 2001.
Attention ! Le sujet peut être modifié jusqu'à la veille du TD.

I.  Les objectifs

II.  Notions abordées dans le cours 4

III.  Références dans les documents

La suite du polycopié "L'héritage et la généricité en C++".

IV.  Conseils généraux

Appelez un intervenant dès que quelquechose ne semble pas clair dans le sujet ou si, après avoir réfléchi, vous ne trouvez pas la solution.

Utilisez si possible les noms de variables et de fonctions donnés dans les sujets pour rester compatible avec les solutions partielles qui vous seront données.

Créez deux fichiers pour chaque classe, et "déclarez" dans le .H et "définissez" dans le .CPP.

Empêchez systématiquement les éventuelles inclusions multiples, déclarez les fonctions 'public', et déclarez les données 'private' et les fonctions 'const' à chaque fois que c'est possible.

Utilisez les fonctions qui accèdent aux données à chaque fois que c'est possible, plutôt que d'accéder aux données directement.

Créez sur C: un répertoire TP4 et des sous-répertoires pour chaque exercice. N'oubliez pas de recopier tous les fichiers sources sur votre compte à la fin du TP.

V.  Exercice 1 (sans rapport avec le distributeur)

  1. Mono-fichier
    1. Récupérer le fichier TESTCARR.CPP dans le répertoire habituel. Créer un fichier CARRE.CPP sans l'ajouter au WorkSpace TESTCARR ; dans un premier temps, CARRE.CPP sera inclus par TESTCARR.CPP.
    2. Définir dans CARRE.CPP une fonction générique carre() permettant de calculer le carré d'une valeur de type quelconque (le résultat sera de même type). Compiler (sans link !) CARRE.CPP jusqu'à ne plus avoir d'erreur.
    3. Construire l'exécutable et tester. Tout doit être OK.
  2. Multi-fichiers
    1. On désire maintenant effectuer une programmation plus propre. Créer CARRE.H qui ne contient que le prototype de la fonction carre(). Modifier TESTCARR.CPP pour qu'il inclue CARRE.H au lieu de CARRE.CPP. Compiler (sans link !) TESTCARR.CPP jusqu'à ne plus avoir d'erreur.
    2. Ajouter CARRE.CPP au WorkSpace et construire l'exécutable. Que se passe-t-il ? Les 2 fichiers .CPP compilent sans erreur mais le link génère des erreurs.
    3. Il faut instancier la fonction générique pour les types qui seront utilisés. Comme carre() n'est pas définie dans le fichier où elle est appelée, il faut ajouter à la fin de CARRE.CPP une ligne template int carre(int); pour chaque type désiré. Ajouter les 3 lignes nécessaires, puis reconstruire ; tout marche !

VI.  Exercice 2 (sans rapport avec le distributeur)

  1. Recopier les classes Point, Pointcol, et Pcoldiag du TP3 (6 fichiers). Récupérer les fichiers TestPoin.CPP, TestPCol.CPP, et TestPCDi.CPP .
  2. Créer un WorkSpace testpcdi et y ajouter les fichiers Point.H, Point.CPP, et TestPoin.CPP . Rendre générique la classe Point afin de pouvoir choisir le type des coordonnées (identique pour les deux).
    Compiler (sans link !) séparément Point.CPP et TestPoin.CPP . Lorsqu'il n'y a plus d'erreur, construire l'exécutable. Que se passe-t-il ? Les 2 fichiers .CPP compilent sans erreur mais le link génère des erreurs. Il faut instancier les classes génériques pour les types qui seront utilisés. Comme Point n'est pas définie dans le fichier où elle est utilisée, il faut ajouter à la fin de POINT.CPP une ligne template class Point<int>; pour chaque type désiré. Ajouter les 2 lignes, puis reconstruire; vérifier les affichages.
  3. Dans le WorkSpace, ajouter Pointcol.H et Pointcol.CPP, et remplacer TestPoin.CPP par TestPCol.CPP . Rendre générique la classe Pointcol afin de pouvoir choisir en plus du type des coordonnées, le type de la couleur. Résoudre le problème d'instanciation des classes génériques, tester, et vérifier les affichages.
  4. Dans le WorkSpace, ajouter Pcoldiag.H et Pcoldiag.CPP, et remplacer TestPCol.CPP par TestPCDi.CPP . Rendre générique la classe Pcoldiag afin de pouvoir choisir le type des coordonnées (rappel: la couleur est imposée). Résoudre le problème d'instanciation des classes génériques, tester, et vérifier les affichages.

VII.  Exercice 3

  1. A priori sans rapport avec le distributeur
    1. Récupérer les fichiers Tabdyn.H, Tabdyn.CPP, et TestDyn.CPP . Comprendre la classe à l'aide des explications ci-dessous. Prévoir les affichages du programme de test. Construire l'exécutable, tester, et comparer les affichages.
    2. Il s'agit d'une classe "tableau dynamique d'entiers" Tabdyn qui alloue la place nécessaire dynamiquement et qui contrôle les indices. La 1ère case du tableau est réservée pour les cas où l'indice est hors limites; il faut donc allouer une case de plus que la taille et décaler l'indice lors des accès au tableau. Cette classe possède 2 données: l'adresse de début et la taille, et 5 méthodes :
      • le constructeur à 1 argument entier (defaut=2) : la taille initiale
      • le destructeur
      • l'accesseur taille()
      • l'opérateur [] 1
      • la procédure agrandit() à 1 argument entier (defaut=2) : la taille ajoutée. Cette procédure alloue la nouvelle zone mémoire, recopie les anciennes valeurs, puis détruit l'ancienne zone.
  2. Adaptation pour le distributeur
    1. Rendre cette classe générique sur le type d'éléments du tableau.
    2. Ne pas oublier d'ajouter ce qu'il faut à la fin de TABDYN.CPP pour pouvoir utiliser des Tabdyn<elem *> et des Tabdyn<int> .
    3. Modifier Testdyn.CPP pour que le tableau t contienne des int et que le tableau u contienne des elem*. Utiliser un cast (conversion) pour régler le problème de compilation.
  3. Distributeur
    1. Recopier tous les fichiers du distributeur à partir du TP3.
    2. Créer un Workspace TestDist et y incorporer tous ces fichiers plus la classe Tabdyn.
    3. Modifier la classe Stock pour qu'elle utilise les Tabdyn.
      .H : une ligne à ajouter au début et 2 lignes à modifier à la fin (attention ! seul le constructeur peut initialiser un objet ...)
      .CPP : une seule ligne à modifier
    4. Vérifier que le distributeur fonctionne toujours.
    5. Remarquer le peu de répercussions qu'a eu ce changement de représentation.

VIII.  Remarque d'approfondissement

Il est normalement nécessaire de définir 2 versions de l'opérateur [] : Ici pour aller plus vite, une seule version a été définie : celle avec const et qui retourne une référence. Pourquoi le const peut-il être ici considéré comme abusif ? Pourquoi le compilateur autorise-t-il le modificateur const ? Pourquoi le Stock fonctionne-t-il correctement alors qu'il lit et e'crit dans les tableaux dynamiques ?

IX.  Travail personnel

Le travail demandé dans les 4 premiers TPs doit être terminé AVANT le prochain TP.



X.  TESTCARR.CPP



// TESTCARR.CPP

#include <iostream.h>
#include <math.h>
#include "carre.cpp" // exercice 1.1
//#include "carre.h" // exercice 1.2

int main()
  {
  int    i=5;
  float  f=1.2f        , prf=1E-6f;
  double d=3.1415926535, prd=1E-13;

  if (carre(i) != i*i)   cout << "ERR";
                  else   cout << "OK";
  cout << " : int" << endl;
  if ( fabs( carre(f) - f*f ) > prf )   cout << "ERR";
                                 else   cout << "OK";
  cout << " : float" << endl;
  if ( fabs( carre(d) - d*d ) > prd )   cout << "ERR";
                                 else   cout << "OK";
  cout << " : double" << endl;
  return 0;
  }
 



XI.  TESTPOIN.CPP



// TESTPOIN.CPP
//
#include <iostream.h>
#include "point.h"

int main()
  {
  Point<int> p1( -1, -3 );  Point<double> p2( 2.1, 4.3 );
             p1.affiche();                p2.affiche();
             p1.deplace( -5, 5 );         p2.deplace( 1.5, -1.5 );
             p1.affiche();                p2.affiche();
  return 0;
  }
/*
DEVRAIT AFFICHER : (-1,-3) (2.1,4.3) (-6,2) (3.6,2.8)
*/
 



XII.  TESTPCOL.CPP



// TESTPCOL.CPP
//
#include <iostream.h>
#include "pointcol.h"

int main()
  {
  Pointcol<int,char>   pc1(1,1,'R');
  Pointcol<double,int>                     pc2(2.1,4.3,9);
                       pc1.affiche();      pc2.affiche();
                       pc1.colore('G');    pc2.colore(7);
                       pc1.affiche();      pc2.affiche();
                       pc1.deplace(-5,5);  pc2.deplace(1.5,-1.5);
                       pc1.affiche();      pc2.affiche();
  return 0;
  }
/*
DEVRAIT AFFICHER :
  R:(1,1) 9:(2.1,4.3) G:(1,1) 7:(2.1,4.3) G:(-4,6) 7:(3.6,2.8)
*/
 

XIII.  TESTPCDI.CPP



// TESTPCDI.CPP
//
#include <iostream.h>
#include "pointcol.h"
#include "pcoldiag.h"

int main()
  {
  Pointcol<double,int> pc1(6.2,8.1,9);
  Pcoldiag<int>                               pcd2( -5 );
                       pc1.affiche();         pcd2.affiche();
                       pc1.colore( 7 );       pcd2.colore( 'G' );
                       pc1.affiche();         pcd2.affiche();
                       pc1.deplace(1.1,2.2);  pcd2.deplace( -5, 5 );
                       pc1.affiche();         pcd2.affiche();
  return 0;
  }
/*
DEVRAIT AFFICHER :
  9:(6.2,8.1) D:(-5,-5) 7:(6.2,8.1) D:(-5,-5) 7:(7.3,10.3) D:(-10,0)
*/
 



XIV.  TABDYN.H



// TABDYN.H
// De'claration de la classe Tabdyn

#ifndef _TABDYN_H
#define _TABDYN_H

class Tabdyn
  {
  public:
	// Constructeur / Destructeur
	Tabdyn( int taille=2 );
	~Tabdyn();
	// Accesseurs
	int taille() const;
	int & operator[]( int ) const;
	// Autres me'thodes
	void agrandit( int );
  private:
	// Donne'es membres
	int * debut;
	int nb_elem;
  };
#endif
 



XV.  TABDYN.CPP



// TABDYN.CPP
// De'finition de la classe Tabdyn

#include <iostream.h>
#include "tabdyn.h"

const int BIDON = 1; // 1e`re case pour indices hors limites

// Constructeur / Destructeur
  Tabdyn::Tabdyn( int taille )
    {
    nb_elem = taille;
    if (nb_elem < 1)
      nb_elem = 1;
    debut = new int[BIDON+nb_elem];
    cout << "Tabdyn[" << nb_elem;
    if (debut)
      cout << "] alloue'." << endl;
    else
      cout << "] non alloue' !" << endl;
    }

  Tabdyn::~Tabdyn()
    {
    delete [] debut;
    }

// Accesseurs
  int Tabdyn::taille() const
    {
    return nb_elem;
    }

  int & Tabdyn::operator[]( int i ) const
    {
    if ( 0<=i && i<taille() )
      return debut[BIDON+i];
    cout << " ERR indice=" << i << " : hors limites !" << endl;
    return debut[BIDON-BIDON];
    }

// Autres me'thodes

  void Tabdyn::agrandit( int n=2 )
    {
    int * tmp = new int[BIDON+taille()+n];
    cout << "Tabdyn[" << (taille()+n);
    if (tmp)
      cout << "] re'alloue'." << endl;
     else
      cout << "] non re'alloue' !" << endl;
    for ( int i=BIDON; i<BIDON+taille(); i++ )
      tmp[i] = debut[i];
    nb_elem += n;
    delete [] debut;
    debut = tmp;
    }
 



XVI.  TESTDYN.CPP



// TESTDYN.CPP
//
#include <iostream.h>
#include "tabdyn.h"

int main()
  {
  Tabdyn t(5); Tabdyn u(32000);
  int i,j;

  if (t.taille()!=5 || u.taille()!=32000)
    cout << "ERREUR sur les tailles !" << endl;
  else
    cout << "Tailles OK." << endl;
  for (i=-1; i<=6; i++)
    { t[i] = 100+i;   u[i] = 200+i; }
  for (j=-1; j<=6; j++)
    cout << t[j] << ',' << u[j] << endl;
  cout << "----------" << endl;
  t.agrandit( 3 );
  for (i=-1; i<=6; i++)
    t[i] = 100+i;
  for (j=-1; j<=6; j++)
    cout << t[j] << ", ";
  cout << endl;
  return 0;
  }
 


File translated from TEX by TTH, version 2.75.
On 4 Dec 2001, 11:49.