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
Par exemple :
- spécialisation / restriction : un Carré est un Rectangle dont longueur et largeur ont la même valeur
- généralisation / extension : un Etudiant est une Personne qui suit des cours
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).
#include <iostream>
#include <string>
using namespace std;
/**
* Definition of a Person
* identified by its name and age
*/
class Person {
// ==============================================
// data members
// ==============================================
protected:
// name of the person (default value is empty string)
string name;
// age of the person (default value is 0)
int age;
// ==============================================
// methods
// ==============================================
public:
/**
* default constructor
*/
Person() {
name = "";
age = 0;
}
/**
* constructor with 2 arguments
* @param n name of the person
* @param a age of the person
*/
Person(string n, int a) {
name = n;
age = a;
}
/**
* getter for name
* @return name of the person
*/
string get_name() {
return name;
}
/**
* getter for age
* @return age of the person
*/
int get_age() {
return age;
}
/**
* setter for name
* @param n name of the person
*/
void set_name(string n) {
name = n;
}
/**
* setter for age
* @param a age of the person
*/
void set_age(int a) {
age = a;
}
/**
* print class contents
* @param output stream reference
* @return output stream reference
*/
ostream& print(ostream& out) {
out << name << ", " << age;
return out;
}
friend ostream& operator<<(ostream& out, Person& p) {
return p.print(out);
}
};
#include "person.h"
/**
* Definition of a Student
* a Student is a Person that is registered in a level of Education
*/
class Student : public Person {
protected:
// name of the level
string level;
public:
/**
* constructor
* @param n name of student
* @param a age of the student
* @param l level
*/
Student(string n, int a, string l) : Person(n, a), level(l) {
}
/**
* getter for level
* @return level of the student
*/
string get_level() {
return level;
}
/**
* set level of student
* @param l level
*/
void set_level(string l) {
level = l;
}
};
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 :
On utilise le mot clé virtual pour éviter d'hériter deux fois des champs de Personne :
#include <iostream>
#include <string>
using namespace std;
/**
* classe Personne
*/
class Personne {
protected:
string nom;
int age;
public:
Personne(string n, int a) : nom(n), age(a) {
cerr << "appel constructeur Personne" << endl;
}
virtual ostream& print(ostream& out) {
out << "Person " << nom << ", " << age;
return out;
}
friend ostream& operator<<(ostream& out, Personne& p) {
return p.print(out);
}
};
/**
* classe Etudiant qui hérite de Personne
* on déclare "public virtual"
*/
class Etudiant : public virtual Personne {
protected:
string formation;
public:
Etudiant(string n, int a, string f) : Personne(n, a), formation(f) {
}
ostream& print(ostream& out) {
out << "Etudiant " << nom << ", " << age << ", " << formation;
return out;
}
};
/**
* classe Enseignant qui hérite de Personne
* on déclare "public virtual"
*/
class Enseignant : public virtual Personne {
protected:
float salaire;
public:
Enseignant(string n, int a, float s) : Personne(n, a), salaire(s) {
}
ostream& print(ostream& out) {
out << "Enseignant " << nom << ", " << age << ", " << salaire;
return out;
}
};
/**
* class Doctorant qui hérite de Etudiant et Enseignant
*/
class Doctorant : public Etudiant, Enseignant {
public:
// on doit appeler le constructeur de Personne en premier
// note: il ne sera appelé qu'une seule fois !
Doctorant(string n, int a, string f, float s) : Personne(n, a),
Etudiant(n, a, f), Enseignant(n, a, s) {
}
ostream& print(ostream& out) {
out << "Doctorant " << nom << ", " << age << ", " << formation << ", " << salaire;
return out;
}
};
int main() {
Doctorant Tom("Thomas", 25, "Doctorat", 1200.00);
cout << Tom << endl;
return 0;
}
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.
#include <iostream>
#include <cstdlib>
using namespace std;
/**
* base class A with virtual destructor
*/
class A {
public:
virtual ~A() { }
};
/**
* class B is a A
*/
class B : public A {
};
/**
* class C is a B
*/
class C : public B {
};
/**
* main
*/
int main() {
A *a = new A;
A *b = new B;
A *c = new C;
// ok, c is of class C that inherits of B
B *p = dynamic_cast<B *>(c);
// not ok ! will return NULL
B *q = dynamic_cast<B *>(a);
cout << "p = " << p << endl;
cout << "q = " << q << endl;
return EXIT_SUCCESS;
}
p = 0x15e1050
q = 0