Copy of https://perso.isima.fr/loic/cpp/tp10.php
tete du loic

 Loïc YON [KIUX]

  • Enseignant-chercheur
  • Référent Formation Continue
  • Responsable des contrats pros ingénieur
  • Référent entrepreneuriat
  • Responsable de la filière F2 ingénieur
  • Secouriste Sauveteur du Travail
mail
loic.yon@isima.fr
phone
(+33 / 0) 4 73 40 50 42
location_on
Institut d'informatique ISIMA
  • twitter
  • linkedin
  • viadeo

[C++] TP 10

 Cette page commence à dater. Son contenu n'est peut-être plus à jour. Contactez-moi si c'est le cas!

Date de première publication : 2013/11/11

Dans ce TP, nous allons nous intéresser à quelques révisions et quelques concepts que nous n'avons pas encore vus.

Références

class MemePasPeur {
   int tab[4];
 public:
   MemePasPeur() {
      tab[0] = 1;
   }
   const int & val() const {
       return tab[0];
   }    
   void modify() {
      tab[0] = 4;
   }
};

int main(int, char **) {
  MemePasPeur * p = new MemePasPeur;
  int i = p->val();
  const int & j = p->val();
  std::cout << i << " " << j << std::endl; 
  p->modify();
  std::cout << i << " " << j << std::endl;
  delete p;
  std::cout << i << " " << j << std::endl;
  return 0;
}

Compilez, exécutez le programme ci-dessus. Que se passe-t-il ? Pourquoi ?

Nous avons évoqué le problème en cours. Il suffit d'exécuter valgrind pour voir l'erreur qui est faite !

Héritage simple

Exercice 1

class Mere {
 public:
  Mere() {
    // cout << "Mere::Mere()" << endl;
    tab = new int[100];
  } 
  ~Mere() {
    // cout << "Mere::~Mere()" << endl;
    delete [] tab;
  }
 protected:
  int * tab;
};

class Fille : public Mere {
 public:
   Fille() {
      // cout << "Fille:Fille()" << endl;
      tab = new double[100];
      //Mere::tab[50] = 4;

   }
   ~Fille() {
    // cout << "Fille::~Fille()" << endl;
    delete [] tab;

  }
 protected:
  double * tab;
};

int main(int, char**) {
  Mere * m = new Fille();

  cout << "penser a valgrind" << endl;
  delete m; 

  return 0;
}

Il ne faut pas toucher à la fonction main(). Le destructeur d'une classe est une méthode comme les autres !

Exercice 2

class Mere {
 public:
  Mere() { m(); }
  virtual void m() { cout << "mm" << endl; }
  virtual ~Mere() {}
};

class Fille : public Mere {
 public:
  Fille() { m(); }
  virtual void m() { cout << "mf" << endl; }
  virtual ~Fille() {}
};

Instanciez la classe Fille. Que constatez-vous ? Qu'est-ce que cela veut dire ?

Pour rappel, voici un exemple de virtualité fonctionnelle :

Mere * o = new Fille;
o->m();

Là encore, c'est un exemple du cours. Il n'y a pas d'appel polymorphique dans un constructeur !

Héritage multiple

L'héritage multiple est très pratique. La classe fille hérite de tous les membres de ses parents. Voici la syntaxe :

class Enfant : public Mere, public Pere {
   // héritage de tous les attributs et méthodes usuels
};

Modèle très simple

Nous devons aider à faire le logiciel de gestion de la société qui fabrique Robocop (OCP pour les intimes). [ Quand on vous dit que l'héritage multiple est un concept utile et très naturel !] La société gère des humanoïdes qui sont à la fois humains et machines.

Concernant les humains, il est nécessaire de connaître le nom, le genre et l'âge. Pour la machine, il est intéressant de connaître le type du modèle, son autonomie, sa réparabilité (note attribuée par iFixit sur 10). Modélisez l'humanoïde avec l'héritage multiple et instanciez quelques produits :-). On pourra s'intéresser à la date de création de l'humanoïde et la date de sa dernière recharge

Voici quelques tests :

TEST_CASE("Humain1") {
  const char * nom ="Alex";
  const Humain alex(nom, HOMME, 35);

  CHECK( nom   == alex.getNom()   );
  CHECK( HOMME == alex.getGenre() );
  // ou enum class Genre.HOMME
  CHECK( 35    == alex.getAge()   );
}

TEST_CASE("Humain2") {
  Humain thomas("thomas", HOMME, 26);

  thomas.setNom("conchita");
  thomas.setAge(27);
  thomas.setGenre(FEMME);
  
  CHECK( "conchita" == thomas.getNom()   );
  CHECK( FEMME      == thomas.getGenre() );
  CHECK( 27         == thomas.getAge()   );
}

TEST_CASE("Machine") {
  Machine stylet("stylet Apple", 2*24*3600, 1);

  CHECK( "stylet Apple" == stylet.getType()      );
  CHECK( 2*24*3600      == stylet.getAutonomie() );
  CHECK( 1              == stylet.getIfixit()    );
}

TEST_CASE("Robocop") {
  Humanoide robocop("Murphy", "Robocop 1.0", HOMME, 35);
    
  CHECK( "Murphy"      == robocop.getNom()    );
  CHECK( "Robocop 1.0" == robocop.getType()   );
  CHECK( HOMME         == robocop.getGenre()  );
  CHECK( 35            == robocop.getAge()    );
  CHECK( 3             == robocop.getIfixit() );  
}
TEST(Humain, Test1) {
  const Humain alex("Alex", HOMME, 35);

  EXPECT_STREQ(alex.getNom().c_str(), "Alex");
  EXPECT_EQ(alex.getGenre(), HOMME);
  EXPECT_EQ(alex.getAge(), 35);
}

TEST(Humain, Test2) {
  Humain thomas("thomas", HOMME, 26);

    thomas.setNom("conchita");
    thomas.setAge(27);
    thomas.setGenre(FEMME);
  
  EXPECT_STREQ(thomas.getNom().c_str(), "conchita");
  EXPECT_EQ(thomas.getGenre(), FEMME);
  EXPECT_EQ(thomas.getAge(), 27);
}

TEST(Machine, Test3) {
  Machine stylet("stylet Apple", 2*24*3600, 1);

  EXPECT_STREQ(stylet.getType().c_str(), "stylet Apple");
  EXPECT_EQ(stylet.getAutonomie(), 2*24*3600);
  EXPECT_EQ(stylet.getIfixit(), 1);
}

TEST(Robocop, Test4) {
  Humanoide robocop("Murphy", "Robocop 1.0", HOMME, 35);
    
  EXPECT_STREQ(robocop.getNom().c_str(), "Murphy");
  EXPECT_STREQ(robocop.getType().c_str(), "Robocop 1.0");
  EXPECT_EQ(robocop.getGenre(), HOMME);
  EXPECT_EQ(robocop.getAge(), 35);
  EXPECT_EQ(robocop.getIfixit(), 3);  
}

Si l'on estime que le nom de l'humain et le type du modèle sont en fait deux informations équivalentes (qui pourraient avoir le même nom dans chacune des classes Humain et Machine) , comment peut-on coder cela avec le minimum de code ?

Mettre en place une classe Commun qui contient les informations communes à Humain et Machine et faire hériter Humain et Machine de Commun.

Un autre modèle

Vous avez été embauché(s, e, es) pour travailler sur un logiciel de recensement. Il faut écrire le noyau de l'application (les classes) en fonction de ce que l'on va vous décrire...

Chaque individu a un nom, une intelligence, une force, une vitesse, une vitalité, ... un potentiel de magie. La population, telle que l'on la connait, est constituée d'humains, d'elfes et d'orcs. Certains individus ont des caractéristiques exceptionnelles et méritent un traitement particulier : les sorciers ou les soldats par exemple.

Pour que la classe ne soit pas instantiable par tout à chacun, il suffit que le ou les constructeurs ne soit(ent) pas public(s) :-)

Nous allons maintenant nous intéresser aux caractéristiques exceptionnelles des individus, par exemple leur faculté à faire de la magie ou à taper (les héros / brutes ...)

Héritage à répétition

class A {
 public:
   A() { cout << "A ";}
  ~A() { cout << "~A ";}
};

class B : public A {
 public:
   B() { cout << "B "; }
  ~B() { cout << "~B ";}
};

class C : public A {
 public:
   C() { cout << "C ";}
  ~C() { cout << "~C ";}
};

class D : public B, public C {
 public:
   D() { cout << "D ";}
  ~D() { cout << "~D ";}
};

int main(int, char**) {
  D d;
  cout << "C'est Fini !";
  return 0 ;
}