04 C++
04 C++
04 C++
1
Compatibilité entre classe de base et
classe dérivée
En POO, on considère d’une manière générale qu’un objet d’une
classe dérivée peut remplacer un objet d’une classe de base
(l’objet de la classe dérivée possède toutes les potentialités pour
remplacer un objet de la classe de base). L’inverse n’est pas vrai.
2
Compatibilité entre classe de base et
classe dérivée
Si B hérite de A, alors toutes les instances de B sont aussi des instances
de A, et il est donc possible de faire :
Aa ; B b; a = b;
// Propriété conservée lorsqu’on utilise des pointeurs :
A *pa; B *pb=&b; pa = pb; /** car pointer sur un B c’est avant tout
pointer sur un A **/
//Evidement , l’inverse n’est pas vrai :
A a; B b; b = a; // ERROR !
// Pareil pour les pointeurs :
A *pa=&a; B *pb; pb=pa; /** ERROR : car pointer sur un A n’est pas
pointer sur un B **/
Conclusion :
Traiter un type dérivé comme s’il était de son type de base est appelé
transtypage ascendant ou surtypage (upcasting).
A l’opposé, les transtypage descendant (downcast) posent un problème
particulier car leur vérification n’est possible qu’à l’exécution.
3
L’édition de lien et le polymorphisme
L’édition de lien est l’opération qui associe, à l’appel d’une
fonction, l’adresse du code de la fonction.
On distingue:
La liaison statique (ou précoce): Quand une méthode à liaison statique
est appelée sur un objet, c’est la méthode correspondant à la classe de
cet objet déterminée au moment de la compilation qui est exécutée.
La liaison dynamique (ou tardive) : Quand une méthode à liaison
dynamique est appelée sur un objet, c’est la méthode correspondant à
la classe réelle de cet objet déterminée au moment de l’exécution qui
est exécutée.
4
Résolution des liens
Une instance de sous-classe B est substituable à une instance de
super-classe A.
class A {
public: void affiche(){cout<<"A"<<endl;} };
class B: public A {
public: void affiche(){cout<<"B"<<endl;}
};
int main ()
{
A aa;
B bb;
aa = bb;
aa.affiche(); // appel affiche() de A ? ou affiche() de B ?
}
5
Résolution des liens
En C++, c’est le type de la variable qui détermine la méthode à
exécuter.
int main ()
Résolution statique des liens. {
A aa;
B bb;
aa = bb;
aa.affiche(); // appel affiche() de A
}
7
Méthodes virtuelles
En C++, on indique au compilateur qu’une méthode peut faire
l’objet d’une résolution dynamique des liens en la déclarant
comme virtuelle (mot clé virtual).
Cette déclaration doit se faire dans la classe la plus générale qui
admet cette méthode (c’est-à-dire lors du prototypage d’origine).
8
Polymorphisme
polymorphisme : possibilité d'utiliser le même code avec différent
types d'objets et se comporte différemment avec chacun.
L'héritage fournit un polymorphisme à l'exécution.
Idée : le code client peut appeler une méthode sur différents types
d'objets, et le comportement résultant sera différent.
9
Polymorphisme : Classe Employee
// Employee.h // Employee.cpp
class Employee { Employee::Employee(string name, int years)
{
public: myName = name;
Employee(string name, int years); virtual int hours() myYears = years;
const; }
virtual string name() const; int Employee::hours() const {
virtual double salary() const; return 40;
virtual int vacationDays() const; virtual string }
vacationForm() const; virtual int years() const; string Employee::name() const {
return myName;
private: }
string myName; double Employee::salary() const {
int myYears; return 40000.0 + (500 * myYears);
}; }
int Employee::vacationDays() const {
return 10;
}
string Employee::vacationForm() const { return "yellow";
}
int Employee::years() const {
return myYears;
}
10
Polymorphisme et Pointeurs
Un pointeur de type T peut pointer sur n'importe quelle sous-classe
de T.
Employee* edna = new Lawyer("Edna", "Harvard", 5);
Secretary* steve = new LegalSecretary("Steve", 2);
11
Polymorphisme : Exemple
En castant vous pouvez utiliser l’extra fonctionnalités de l’objet
Employee* edna = new Lawyer("Edna", "Harvard", 5); // ok
edna->vacationDays();
edna->sue("Stuart"); // compiler error
((Lawyer*) edna)->sue("Stuart"); // ok
Vous ne devriez pas casté un pointeur vers quelque chose qu'il ne
l'est pas. Il compilera, mais le code plantera (ou se comportera de
manière imprévisible) lorsque vous essayerez de l'exécuter
Employee* paul = new Programmer("Paul", 3);
paul->code(); // compiler error
((Programmer*) paul)->code(); // ok
((Lawyer*) paul)->sue("Marty"); // crash!
12
Mystère du polymorphisme
class Snow { class Sleet : public Snow {
public: public:
virtual void method2() { virtual void method2() {
cout << "Snow 2" << endl; cout << "Sleet 2" << endl;
} Snow::method2();
virtual void method3() { }
cout << "Snow 3" << endl; virtual void method3() {
} cout << "Sleet 3" << endl;
}; }
class Rain : public Snow { };
public: virtual void method1() { class Fog : public Sleet {
cout << "Rain 1" << endl; public: virtual void method1() {
} cout << "Fog 1" << endl;
virtual void method2() { }
cout << "Rain 2" << endl; virtual void method3() {
} cout << "Fog 3" << endl;
}; }
};
13
Diagramme de Classes
Diagramme de
classes du haut Snow
(superclasse) en method2()
Snow 2
Rain Sleet
method1() Rain 1 method2() Sleet 2 \ Snow 2
method2() Rain 2 method3() Sleet 3
(method3()) Snow 3
Fog
method1() Fog 1
method2() Sleet 2 \ Snow
method3() Fog 3
14
Problème Mystère
Snow* var1 = new Sleet();
var1->method2(); // What's the output?
15
Diagramme de Classes
Snow* var1 = new Sleet();
Snow
var1->method2();
Snow 2
method2()
method3() Snow 3
Output? :
A. Snow 2
B. Rain 2 Fog
C. Sleet 2 / Snow 2 method1() Fog 1
D. COMPILER ERROR method2() Sleet 2 / Snow
method3() Fog 3
16
Diagramme de Classes
Snow* var2 = new Rain();
Snow
var2->method1();
Snow 2
method2()
method3() Snow 3
Output? :
A. Snow 1
B. Rain 1 Fog
C. Snow 1 /Rain 1 method1() Fog 1
D. COMPILER ERROR method2() Sleet 2 / Snow
method3() Fog 3
17
Diagramme de Classes
Snow* var3 = new Rain();
Snow
var3->method2();
Snow 2
method2()
method3() Snow 3
Output? :
A. Snow 2
B. Rain 2 Fog
C. Snow 2 /Rain 2 method1() Fog 1
D. COMPILER ERROR method2() Sleet 2 / Snow
method3() Fog 3
18
Conversion de type (cast)
19
Diagramme de Classes
Snow* var4 = new Rain();
((Rain *) var4)->method1(); Snow
Snow 2
method2()
method3() Snow 3
Output? :
A. Snow 1
B. Rain 1 Fog
C. Sleet 1 method1() Fog 1
D. COMPILER ERROR method2() Sleet 2 / Snow
method3() Fog 3
20
Diagramme de Classes
Snow* var5 = new Fog();
((Sleet*) var5)->method1(); Snow
Snow 2
method2()
method3() Snow 3
Output? :
A. Snow 1 objet
B. Sleet 1 Fog
C. Fog 1 method1() Fog 1
D. COMPILER ERROR method2() Sleet 2/Snow 2
method3() Fog 3
21
Diagramme de Classes
Supposons qu’on a ajouté la
Méthode suivante à la classe de base Snow
Snow : method2() Snow 2
virtual void method4() { method3() Snow 3
cout << "Snow 4" << endl; method2();
}
Rain Sleet objet
method1() Rain 1 method2() Sleet 2/Snow 2
method2() Rain 2 method3() Sleet 3
(method3()) Snow 3
Output? :
A. Snow 1
B. Sleet 1 Fog
C. Fog 1 method1() Fog 1
D. COMPILER ERROR method2() Sleet 2/Snow 2
method3()
E. CRASH / UNDEFINED Fog 3
23
Virtual : constructeurs et destructeurs
Les constructeurs ne peuvent pas être virtuels : déclarer
un constructeur comme virtual est une erreur de
compilation.
24
Fonction virtuelle pure
25
Templates
Forme générale
#include "Point.h"
int main()
P1.Affiche();
P2.Affiche();
P3.Affiche();
return 0;