1. Introduction au C++



sommaire chapitre 1

1.7. Héritage, Interface

L'héritage permet la réutilisabilité du code et produit des classes spécialisées ou généralistes.

class Fille : [virtual] public/protected/private Classe_Mère {

  ...	

};

1.7.1. Héritage simple

On utilise l'héritage quand on peut dire qu'une classe B est une classe A avec certaines propriétés en plus ou avec certaines restrictions.

B est appelée classe Fille et A est appelée la classe Mère

hierarchie de classes

Par exemple :

Exemple : on crée une classe Person, puis une classe Student qui hérite de Person car Student est une Person inscrite dans une formation (level).

  1. #include <iostream>
  2. #include <string>
  3. using namespace std;
  4.  
  5. /**
  6.  * Definition of a Person
  7.  * identified by its name and age
  8.  */
  9. class Person {
  10. // ==============================================
  11. // data members
  12. // ==============================================
  13. protected:
  14.     // name of the person (default value is empty string)
  15.     string name;
  16.     // age of the person (default value is 0)
  17.     int age;
  18.  
  19. // ==============================================
  20. // methods
  21. // ==============================================
  22. public:
  23.     /**
  24.      * default constructor
  25.      */
  26.     Person() {
  27.         name = "";
  28.         age = 0;
  29.     }
  30.    
  31.     /**
  32.      * constructor with 2 arguments
  33.      * @param n name of the person
  34.      * @param a age of the person
  35.      */
  36.     Person(string n, int a) {
  37.         name = n;
  38.         age = a;
  39.     }
  40.    
  41.     /**
  42.      * getter for name
  43.      * @return name of the person
  44.      */
  45.     string get_name() {
  46.         return name;
  47.     }
  48.    
  49.     /**
  50.      * getter for age
  51.      * @return age of the person
  52.      */
  53.     int get_age() {
  54.         return age;
  55.     }
  56.    
  57.     /**
  58.      * setter for name
  59.      * @param n name of the person
  60.      */
  61.     void set_name(string n) {
  62.         name = n;
  63.     }
  64.    
  65.     /**
  66.      * setter for age
  67.      * @param a age of the person
  68.      */
  69.     void set_age(int a) {
  70.         age = a;
  71.     }
  72.    
  73.     /**
  74.      * print class contents
  75.      * @param output stream reference
  76.      * @return output stream reference
  77.      */
  78.     ostream& print(ostream& out) {
  79.         out << name << ", " << age;
  80.         return out;
  81.     }
  82.    
  83.     friend ostream& operator<<(ostream& out, Person& p) {
  84.         return p.print(out);
  85.     }
  86. };
  87.  
  1. #include "person.h"
  2.  
  3. /**
  4.  * Definition of a Student
  5.  * a Student is a Person that is registered in a level of Education
  6.  */
  7. class Student : public Person {
  8. protected:
  9.     // name of the level
  10.     string level;
  11.    
  12. public:
  13.     /**
  14.      * constructor
  15.      * @param n name of student
  16.      * @param a age of the student
  17.      * @param l level
  18.      */
  19.     Student(string n, int a, string l) : Person(n, a), level(l) {
  20.     }
  21.    
  22.     /**
  23.      * getter for level
  24.      * @return level of the student
  25.      */
  26.     string get_level() {
  27.         return level;
  28.     }
  29.    
  30.     /**
  31.      * set level of student
  32.      * @param l level
  33.      */
  34.     void set_level(string l) {
  35.         level = l;
  36.     }
  37.    
  38.    
  39. };
  40.  

La classe Student n'a pas besoin de redéfinir les getters/setters de la classe mère Person.

En terme d'interface, on a donc :

class Person {
  Person();
  Person(string n, int a);
  ~Person();
  string get_name();
  int get_age();
  void set_name(string n);
  void set_age(int a);
  virtual ostream& print(ostream& out);
  friend ostream& operator<<(ostream& out, Person& p);
};

class Student : public Person {
  Student(string n, int a, string l);
  string get_level() 
  void set_level(string l);
  ostream& print(ostream& out);
};

Note : on a fait figurer en rouge dans la partie ci-dessus ce qui devrait être ajouté ou modifié afin d'aboutir à une meilleure conception des classes.

1.7.2. Héritage multiple

L'héritage multiple correspond au cas où une classe B hérite de plusieurs autres classes A_1, ..., A_n.

Un problème survient lorsqu'une classe hérite deux fois de la même classe mère par le biais de deux classes filles : c'est le cas de l'héritage en diamant :

heritage diamant

On utilise le mot clé virtual pour éviter d'hériter deux fois des champs de Personne :

  1. #include <iostream>
  2. #include <string>
  3. using namespace std;
  4.  
  5. /**
  6.  * classe Personne
  7.  */
  8. class Personne {
  9. protected:
  10.     string nom;
  11.     int age;
  12. public:
  13.     Personne(string n, int a) : nom(n), age(a) {
  14.         cerr << "appel constructeur Personne" << endl;
  15.     }
  16.     virtual ostream& print(ostream& out) {
  17.         out << "Person " << nom << ", " << age;
  18.         return out;
  19.     }
  20.     friend ostream& operator<<(ostream& out, Personne& p) {
  21.         return p.print(out);
  22.     }
  23. };
  24.  
  25. /**
  26.  * classe Etudiant qui hérite de Personne
  27.  * on déclare "public virtual"
  28.  */
  29. class Etudiant : public virtual Personne {
  30. protected:
  31.     string formation;
  32. public:
  33.     Etudiant(string n, int a, string f) : Personne(n, a), formation(f) {
  34.     }
  35.     ostream& print(ostream& out) {
  36.         out << "Etudiant " << nom << ", " << age << ", " << formation;
  37.         return out;
  38.     }
  39. };
  40.  
  41. /**
  42.  * classe Enseignant qui hérite de Personne
  43.  * on déclare "public virtual"
  44.  */
  45. class Enseignant : public virtual Personne {
  46. protected:
  47.     float salaire;
  48. public:
  49.     Enseignant(string n, int a, float s) : Personne(n, a), salaire(s) {
  50.     }
  51.     ostream& print(ostream& out) {
  52.         out << "Enseignant " << nom << ", " << age << ", " << salaire;
  53.         return out;
  54.     }
  55. };
  56.  
  57. /**
  58.  * class Doctorant qui hérite de Etudiant et Enseignant
  59.  */
  60. class Doctorant : public Etudiant, Enseignant {
  61. public:
  62.     // on doit appeler le constructeur de Personne en premier
  63.     // note: il ne sera appelé qu'une seule fois !
  64.     Doctorant(string n, int a, string f, float s) : Personne(n, a),
  65.         Etudiant(n, a, f), Enseignant(n, a, s) {
  66.     }
  67.     ostream& print(ostream& out) {
  68.         out << "Doctorant " << nom << ", " << age << ", " << formation << ", " << salaire;
  69.         return out;
  70.     }
  71. };
  72.  
  73. int main() {
  74.     Doctorant Tom("Thomas", 25, "Doctorat", 1200.00);
  75.    
  76.     cout << Tom << endl;
  77.     return 0;
  78. }
  79.  
  80.  

Dans la classe Doctorant, le constructeur doit appeler le constructeur de Personne, puis les constructeurs des classes Etudiant et Enseignant. Cependant le constructeur de Personne ne sera appelé qu'une seule fois.

1.7.3. dynamic_cast

L'opérateur dynamic_cast permet de transformer un pointeur sur un objet dans une hierarchie de classes. Si la conversion est impossible alors dynamic_cast retournera NULL.

hierarchie de classes

  1. #include <iostream>
  2. #include <cstdlib>
  3. using namespace std;
  4.  
  5. /**
  6.  * base class A with virtual destructor
  7.  */
  8. class A {
  9. public:
  10.     virtual ~A() { }
  11. };
  12.  
  13. /**
  14.  * class B is a A
  15.  */
  16. class B : public A {
  17. };
  18.  
  19. /**
  20.  * class C is a B
  21.  */
  22.  
  23. class C : public B {
  24. };
  25.  
  26. /**
  27.  * main
  28.  */
  29. int main() {
  30.     A *a = new A;
  31.     A *b = new B;
  32.     A *c = new C;
  33.    
  34.     // ok, c is of class C that inherits of B
  35.     B *p = dynamic_cast<B *>(c);
  36.    
  37.     // not ok ! will return NULL
  38.     B *q = dynamic_cast<B *>(a);
  39.    
  40.     cout << "p = " << p << endl;
  41.     cout << "q = " << q << endl;
  42.    
  43.     return EXIT_SUCCESS;
  44. }
  45.  
p = 0x15e1050
q = 0