public:
. . .
void AfficherCaracteristiques() const;
};
• Définition
void Condensateur::AfficherCaracteristiques() const {
ComposantPassif::AfficherCaracteristiques();
cout << "Capacite : " << capacite * 1.0e6 << " uF" << endl;
}
• Résultat
Programmation orientée objet en C++
Héritage – Représentation en mémoire
ComposantPassif string nom
Condensateur string nom
double capacite
Organisation en mémoire identique
Conséquences – affectations possibles
compo1 = c1; // Ok, définit entièrement compo1
c1 = compo1; // Interdit, ne définit pas complètement c1
Introduction à la programmation orientée objet avec C++ 94
Les méthodes virtuelles et le polymorphisme - Introduction
• On souhaite écrire une fonction
– Calculant et affichant les valeurs d’impédances à toutes les décades (1, 10, 100, 1000, 10000 Hz…)
– Utilisable avec tous types d’impédances
• Résistances, Condensateurs, Inductances, autres…
• Prototype pour une résistance
void AfficherValeursImpedance(const Resistance & composant);
• Prototype pour un condensateur
void AfficherValeursImpedance(const Condensateur & composant);
• Prototype pour une inductance
void AfficherValeursImpedance(const Inductance & composant);
Programmation orientée objet en C++
Les méthodes virtuelles et le polymorphisme - Introduction
• Implémentation : 3 fois le même code !
void AfficherImpedance(const Resistance & composant) {
double frequence = 1.0;
Complexe impedance;
composant.AfficherCaracteristiques();
while (frequence <= 1.0e6) {
impedance = composant.CalculerImpedance(frequence);
cout << frequence << " Hz : " << impedance << endl;
frequence = frequence * 10;
} }
void AfficherImpedance(const Condensateur & composant) {
double frequence = 1.0;
Complexe impedance;
composant.AfficherCaracteristiques();
while (frequence <= 1.0e6) {
impedance = composant.CalculerImpedance(frequence);
cout << frequence << " Hz : " << impedance << endl;
frequence = frequence * 10;
} }
void AfficherImpedance(const Inductance & composant) {
double frequence = 1.0;
Complexe impedance;
composant.AfficherCaracteristiques();
while (frequence <= 1.0e6) {
impedance = composant.CalculerImpedance(frequence);
cout << frequence << " Hz : " << impedance << endl;
frequence = frequence * 10;
} }
Introduction à la programmation orientée objet avec C++ 96
Les méthodes virtuelles et le polymorphisme - Introduction int main()
{
Condensateur c1;
Inductance l1;
Resistance r1;
r1.DefinirNom("Resistance 1");
r1.DefinirResistance(1000);
c1.DefinirNom("Condensateur 1");
c1.DefinirCapacite(4.7e-6); // 4.7 µF l1.DefinirNom("Inductance 1");
l1.DefinirInductance(25e-3); // 25 mH AfficherImpedance(r1);
AfficherImpedance(c1);
AfficherImpedance(l1);
pause();
}
Programmation orientée objet en C++
Les méthodes virtuelles et le polymorphisme
• Solution pour éviter de répéter 3 fois le code ?
– Utiliser le type de base ComposantPassif comme paramètre.
– Résistance, Condensateur et Inductance héritent de ComposantPassif.
• Problème
– « CalculerImpedance » n’est pas déclarée sur ComposantPassif !
void AfficherImpedance(const ComposantPassif & composant) {
double frequence = 1.0;
Complexe impedance;
composant.AfficherCaracteristiques();
while (frequence <= 1.0e6) {
impedance = composant.CalculerImpedance(frequence);
cout << frequence << " Hz : " << impedance << endl; + AfficherCaracteristiques() : void
Induc tance - inductance : double
+ CalculerImpedance(double) : Complexe
Introduction à la programmation orientée objet avec C++ 98
Les méthodes virtuelles et le polymorphisme
• Déclarer CalculerImpedance pour la classe parent
class ComposantPassif {
public:
. . . .
Complexe CalculerImpedance(double frequence) const;
};
Complexe ComposantPassif::CalculerImpedance(double frequence) const {
Complexe resultat;
resultat.DefinirCartesien(0.0, 0.0);
return resultat;
}
• On choisit de retourner 0, car on ne sait pas comment calculer dans la classe ComposantPassif.
Programmation orientée objet en C++
Les méthodes virtuelles et le polymorphisme – Test
void AfficherImpedance(const ComposantPassif & composant) {
double frequence = 1.0;
Complexe impedance;
composant.AfficherCaracteristiques();
while (frequence <= 1.0e6) {
impedance = composant.CalculerImpedance(frequence);
cout << frequence << " Hz : " << impedance << endl;
r1.DefinirNom("Resistance 1");
r1.DefinirResistance(1000);
c1.DefinirNom("Condensateur 1");
c1.DefinirCapacite(4.7e-6); // 4.7 µF l1.DefinirNom("Inductance 1");
l1.DefinirInductance(25e-3); // 25 mH AfficherImpedance(r1);
AfficherImpedance(c1);
AfficherImpedance(l1);
pause();
}
Introduction à la programmation orientée objet avec C++ 100
Les méthodes virtuelles et le polymorphisme – Observations
• Fonctionnement non satisfaisant
– AfficherCaracteristiques
• C’est la méthode de base de ComposantPassif qui est appelée.
• Elle n’affiche que le nom du composant, pas ses valeurs.
– CalculerImpedance
• C’est la méthode de base de ComposantPassif qui est appelée.
• Elle retourne toujours 0.
• Comportement souhaitable
– En fonction de la classe du composant passé en paramètre.
– Appeler la fonction AfficherCaracteristiques de cette classe.
– Appeler la fonction CalculerImpedance de cette classe.
Programmation orientée objet en C++
Les méthodes virtuelles et le polymorphisme
• Appel d’une méthode normale
– La méthode appelée est celle associée au type de la déclaration – Cette méthode est choisie au moment de la compilation.
• Appel d’une méthode virtuelle
– La méthode appelée est celle associée au type effectif de l’objet passé en paramètre.
– Cette méthode est choisie au moment de l’exécution.
• Syntaxe
– Pour qu’une méthode soit virtuelle, il suffit de faire précéder sa déclaration par le mot réservé « virtual ».
– En C++, cette déclaration est requise seulement à la première apparition de la méthode (dans la classe de base).
Introduction à la programmation orientée objet avec C++ 102
Les méthodes virtuelles et le polymorphisme
• Modification minime de l’exemple précédent
class ComposantPassif {
private:
string nom;
public:
void DefinirNom(const string & valeur);
string LireNom() const;
virtual void AfficherCaracteristiques() const;
virtual Complexe CalculerImpedance(double frequence) const;
};
• Exécution conforme aux attentes
Programmation orientée objet en C++
Les méthodes virtuelles et le polymorphisme
• Avec la déclaration virtual
– La fonction CalculerImpedance appelée est celle correspondant au type effectif de l’objet.
– Idem pour AfficherCaracteristiques.
void AfficherImpedance(const ComposantPassif & composant) {
double frequence = 1.0;
Complexe impedance;
composant.AfficherCaracteristiques();
while (frequence <= 1.0e6) {
impedance = composant.CalculerImpedance(frequence);
cout << frequence << " Hz : " << impedance << endl;
frequence = frequence * 10;
} }
Introduction à la programmation orientée objet avec C++ 104
Les méthodes virtuelles et le polymorphisme
void AfficherImpedance(const ComposantPassif & composant) {
double frequence = 1.0;
Complexe impedance;
composant.AfficherCaracteristiques();
while (frequence <= 1.0e6) {
impedance = composant.CalculerImpedance(frequence);
cout << frequence << " Hz : " << impedance << endl;
frequence = frequence * 10;
} }
La méthode effectivement appelée derrière cet appel dépend du type effectif de l’objet.
La fonction AfficherImpedance s’adapte au type des objets manipulés.
Cette fonction peut être utilisée avec différents types, elle est dite polymorphe.
Le mécanisme utilisé s’appelle polymorphisme.
Programmation orientée objet en C++
Les méthodes virtuelles et le polymorphisme - Fonctionnement
• Une classe avec des méthodes virtuelles
– Comporte automatiquement une VMT : Virtual Method Table
– C’est un tableau contenant l’adresse de ses différentes méthodes virtuelles
• Un objet d’une classe avec méthodes virtuelles
– Comporte automatiquement un pointeur sur la VMT de sa classe (_vfptr, _vptr, _vmt)
class A
VMTComposant
Les méthodes virtuelles et le polymorphisme - Fonctionnement
Introduction à la programmation orientée objet avec C++ 106
ComposantPassif
VMTComposantPassif* __vmt;
VMTResistance
VMTInductance Resistance
VMTComposantPassif* __vmt;
Inductance
VMTComposantPassif* __vmt;
Programmation orientée objet en C++
Les méthodes virtuelles et le polymorphisme - Fonctionnement
• Appel d’une méthode virtuelle
– On accède à la VMT de la classe grâce au champs caché _vmt.
– On trouve l’adresse de la méthode dans la VMT par position
• Chaque méthode correspond à une position fixe
• Ex : VMT[0] : AfficherCaracteristiques
– On appelle la méthode se trouvant à cette adresse
• Coût
– Performances : 1 indirection, donc peu d’impact – Mémoire :
• Tables VMT répétées pour chaque classe.
• Code plus volumineux
• Problématique dans des environnements extrêmement contraints.
Les méthodes virtuelles et le polymorphisme - Fonctionnement
• Observation avec le debugger visual studio.
Inductance l1, l2;
Condensateur c1, c2;
Resistance r1, r2;
Introduction à la programmation orientée objet avec C++ 108
Programmation orientée objet en C++
Les méthodes virtuelles et le polymorphisme
• Classe ComposantPassif
– La déclaration de la méthode CalculerImpedance est requise pour permettre son appel sur les objets de ce type.
– Son implémentation retournant toujours 0 est inutile.
– Le seul intérêt est de déclarer l’ « interface » commune de la classe Composant Passif et de ses sous-classes.
• Méthode virtuelle pure (abstraite)
– Il est possible de déclarer la méthode sans l’implémenter.
– Elle est alors dite virtuelle pure ou abstraite.
– Il faut terminer la déclaration par « =0 ».
• Exemple
virtual Complexe CalculerImpedance(double frequence) const =0;
Introduction à la programmation orientée objet avec C++ 110
Polymorphisme avec des paramètres par adresse ou par référence
• Passage par adresse ou par référence
void AfficherImpedance(const ComposantPassif & composant);
– La fonction reçoit l’adresse du paramètre effectif.
– Permet d’aller consulter sa classe et d’appeler la bonne méthode virtuelle.
• Passage par valeur
void AfficherImpedance(const ComposantPassif composant);
– La fonction reçoit une copie du paramètre effectif.
– La copie est toujours de type ComposantPassif, pas du type effectif.
– Seules les méthodes de ComposantPassif seront appelées.
– Le mécanisme des méthodes virtuelles ne fonctionne pas !
Programmation orientée objet en C++
Une 3ème classe de visibilité
• Rappel
– public
• Méthodes et attributs utilisables depuis l’extérieur de la classe.
– private
• Méthodes et attributs utilisables depuis l’intérieur de la classe.
• Classe de visibilité supplémentaire
– protected : méthode et attributs utilisables
• Depuis l’intérieur de la classe.
• Depuis l’intérieur des sous classes.
Introduction à la programmation orientée objet avec C++ 112
Polymorphisme avec des listes d’objet
• Possibilité de construire des listes d’objets polymorphes
– Liste contenant des pointeurs sur des objets de différents types.
• Exemple : liste de pointeurs sur ComposantPassif
Resistance Resistance
Condensateur Liste de
ComposantPassif *
Inductance
Condensateur
Inductance
Condensateur Resistance
Condensateur
Programmation orientée objet en C++
Signification conceptuelle de l’héritage
• Quand est-il juste d’utiliser un héritage ?
– Si on peut dire de la sous-classe « EST UN » par rapport à son parent
ComposantPassif - nom: string
+ DefinirNom(st ring) : void + LireNom() : string + AfficherCaracteristiques() : void
Resis tance - resistance : double
+ CalculerImpedance(double) : Complexe
Induc tance - inductance : double
+ CalculerImpedance(double) : Complexe
Condensateur - capacite: double
+ CalculerImpedance(double) : Complexe
Introduction à la programmation orientée objet avec C++ 114
Principes de conception logicielle orientée objet
• Que signifie classe ?
– Classe = ensemble d’objets.
– Terme issu du domaine de la « Classification ».
– Sous-classe = sous-ensemble.
ComposantPassif - nom: string
+ DefinirNom(st ring) : void + LireNom() : string + AfficherCaracteristiques() : void
Resis tance - resistance : double
+ CalculerImpedance(double) : Complexe
Induc tance - inductance : double + CalculerImpedance(double) : Complexe
Condensateur - capacite: double
+ CalculerImpedance(double) : Complexe
Composant passif
Résistance
Inductance
Condensateur
Programmation orientée objet en C++
Principes de conception logicielle orientée objet - UML
EtreV iv ant
Végé tal Ani mal
Vertébrés Inv ertébré
Mamifères
Hum ain
• Conception orientée objet :
– Découpage d’un problème complexe en familles d’objets.
– Notion d’objet : identité, état, comportement.
– Notion assez intuitive.
• Caractéristique des logiciels conçus selon une approche Orientée Objet
– Résistent assez bien aux changements de spécifications.
– Aisément extensibles.
– Meilleure pérennité.
Introduction à la programmation orientée objet avec C++ 116
Concepts avancés – la généricité - introduction
• Exemple des classes de gestion de liste
– ListeDouble, ListeInt, ListeChar, ListComposantPassif, … – Le code est strictement identique.
– Seul le type des éléments change.
• Comment éviter de répéter le code pour chaque type ?
• C++ supporte la généricité
– Possibilité d’écrire des classes et fonctions.
– Dont l’implémentation est paramétrée par le type.
• Exemple
– List<double>
– List<int>
Le langage C++
Concepts avancés – la généricité – déclaration d’une classe générique
• Particularité
– La définition des méthodes doit aussi être visible lors de l’utilisation de la classe générique
– En général, placé aussi dans le .h
template <class ElementType, int Size>
class List { public:
List();
ElementType & operator[](int index);
int GetSize() const { return Size; } int GetCount() const;
int Add(const ElementType & value);
void Clear();
void RemoveAt(int index);
void Insert(int index, const ElementType & value);
private:
ElementType _elements[Size];
int _count;
};
Introduction à la programmation orientée objet avec C++ 118
Concepts avancés – la généricité – implémentation
template <class ElementType, int Size>
void List<ElementType, Size>::RemoveAt(int index) {
if (index >= 0 && index < _count) {
for (int i = index + 1 ; i < _count; i++) _elements[i - 1] = _elements[i];
_count = count - 1;
} else
throw Exception("Invalid index");
}
• Remarques
– Une classe générique est appelée « template » ou « patron ».
– C’est un moule paramétré permettant de générer des classes.
– La généricité peut aussi s’appliquer aux fonctions.
Le langage C++
Concepts avancés – la généricité – utilisation
List<double, 100> liste1;
List<int, 100> liste2;
liste1.Add(2.3);
liste2.Add(10);
Introduction à la programmation orientée objet avec C++ 120
Concepts avancés – la stl
• Le C++ est livré avec une bibliothèque de templates
– Stl : Standard Template Library.
– Avantages
• Comporte de nombreux conteneurs et algorithmes.
– Inconvénients
• Utilisation peu intuitive au début.
• Pas facile à étendre.
• Utilise l’allocation dynamique de mémoire.
• Difficilement utilisable dans les environnements temps réel.
Le langage C++
Concepts avancés – la stl - exemple
#include <iostream>
#include <vector>
using namespace std;
int main() {
vector<double> v;
v.push_back(1.1);
v.push_back(2.2);
v.push_back(3.3);
cout << "Taille: " << v.size() << endl;
cout << "Element[1]:" << v[1] << endl;
cout << "Boucle sur les elements:" << endl;
int i;
for(i=0; i < v.size(); i++) cout << v[i] << endl;
cout << endl << "Iterateur:" << endl;
vector<double>::const_iterator it;
for(it = v.begin() ; it != v.end() ; it++) cout << *it << endl;
Introduction à la programmation orientée objet avec C++ 122
Concepts avancés – la stl – aperçu du contenu
Le langage C++
Bibliothèque standard
• Bibliothèque standard du C++ : assez fournie.
– Chaînes de caractères.
– Entrées sorties, fichiers.
– Conteneurs génériques : Standard Template Library (STL)
• Vecteurs, listes, files, piles, …
• Largement documentée
Introduction à la programmation orientée objet avec C++ 124