Vous êtes sur la page 1sur 64

Programmation orientée objet

avec C++

ACOO Analyse, Conception et développement Orientés Objet de logiciels de commande

Thèmes abordés
• Présentation du langage C++
– Principales différences par rapport au C.
• Orientation objet
– Notion de classe et d’objet
– Encapsulation, public, privé.
– Héritage, accès protégé.
– Polymorphisme.
• Eléments avancés
– Programmation générique.
– Traitement d’erreurs par exception.
– Surcharge d’opérateurs.

Introduction à la programmation orientée objet avec C++ 1

1
Le langage C++
Introduction

• Historique
– Bjarne Stroustrup crée en 1979 « C With Classes »
– Renommé C++ en 1983
– C++: incrémentation, le langage après C

– Normalisé par ISO

• Que définit C++ ?


– Un langage orienté objet basé sur le C.
• Syntaxe, instructions et code généré similaires.
• Quelques incompatibilités avec C.
• Support de l’héritage, polymorphisme, généricité.
– Une bibliothèque standard
• Standard C++ library

Introduction à la programmation orientée objet avec C++ 2

Le langage C++
Créer un programme C++ avec Visual Studio

• Donner l’extension .cpp au fichier source !

Introduction à la programmation orientée objet avec C++ 3

2
Le langage C++
Les entrées sorties standard du C++ Bibliothèque d’entrées sorties
du C++
#include <iostream>
Flux de sortie
int main()
{
Opérateur surchargé en C++.
double x, y; Ecriture vers un flux.

std::cout << "X:"; Flux d’entrée.


std::cin >> x;
std::cout << "Y:"; Opérateur surchargé en C++.
std::cin >> y; Lecture depuis un flux.

std::cout << "x * y = " << x * y << std::endl;


return 0;
} Opérateur pouvant être
chaîné

Introduction à la programmation orientée objet avec C++ 4

Le langage C++
Les espaces de nom - utilisation

• Intérêt des espaces de nom


– Grands projets avec de nombreuses bibliothèques
• On pourrait avoir plusieurs bibliothèques contenant des fonctions
différentes avec des noms identiques.
• Impossibilité de construire un tel programme en C.
– Espace de nom
• Préfixe tous les identificateurs de l’espace avec un nom.
• Exemple dans l’espace std : std::cout
• Simplification d’écriture
– Il est possible d’importer un espace de nom.
– Permet d’utiliser les identificateurs de cet espace sans préfixe.
• Syntaxe
using namespace std;

Introduction à la programmation orientée objet avec C++ 5

3
Le langage C++
Les espaces de nom – utilisation - exemple
#include <iostream>
#include <conio.h>

// Importation de l'espace de nom :


using namespace std;

int main()
{
double x, y;

// Le préfixe n'est plus requis :


cout << "X:";
cin >> x;
cout << "Y:";
// Il est toujours possible d'utilier le préfixe :
std::cin >> y;
cout << "x * y = " << x * y << endl;
// Les fonctions de la bibliothèque C sont toujours utilisables
_getch();
return 0;
}

Introduction à la programmation orientée objet avec C++ 6

Le langage C++
Les espaces de nom - définition

• Il est possible de définir un espace de nom.


• Les identificateurs de cet espace sont alors préfixés.
• Syntaxe
namespace nom
{

// Placer ici les déclarations faisant partie de


// l'espace de nom

Introduction à la programmation orientée objet avec C++ 7

4
Le langage C++
Les espaces de nom – définition - exemple
namespace heig_vd
{
void afficher_adresse()
{
cout << "HEIG-VD" << endl;
cout << "Route de Cheseaux 1" << endl <<
"1401 Yverdon les bains" << endl;
}

void afficher_coordonnees_completes()
{
// Préfixe non requis, car dans le même espace de nom :
afficher_adresse();
cout << "Tel : 024 55 76 330 \n";
}
}

int main()
{
heig_vd::afficher_coordonnees_completes();
return 0;
}

Introduction à la programmation orientée objet avec C++ 8

Le langage C++
Les types supplémentaires du C++ - bool

• C++ introduit un type pour les conditions logiques


• Son utilisation est recommandée, mais pas obligatoire.

bool valide;

valide = true;

valide = false;
if (valide)
. . .

Introduction à la programmation orientée objet avec C++ 9

5
Le langage C++
Les types supplémentaires du C++ - string
• Chaînes de caractères
– Pas de type défini au niveau du langage C++ (idem C).
– Implémenté sous la forme d’une classe dans la bibliothèque standard <string>
• A ne pas confondre avec string.h
– Manipulation beaucoup plus commode qu’avec les tableaux de caractères.
• Exemple
#include <iostream>
#include <string>

using namespace std;

int main()
{
string nom;
cout << "Votre nom : ";
// cin >> nom; : ne saisit que le 1er mot. Utiliser getline
getline(cin, nom);
cout << "Bonjour " << nom << endl;

char c;
cin >> c;
}
Introduction à la programmation orientée objet avec C++ 10

Le langage C++
Les types supplémentaires du C++ - string – quelques opérations
#include <iostream>
#include <string>

using namespace std;

int main()
{
string chaine1, chaine2;

chaine1 = "Le loup et l'agneau";


// longueur de la chaine:
cout << chaine1.length() << endl;
// le caractère en position 5:
cout << chaine1.at(5) << endl;
// sous-chaine de 4 caractères à partir de la position 3
chaine2 = chaine1.substr(3, 4);
cout << chaine2 << endl;

char c;
cin >> c;
}

Introduction à la programmation orientée objet avec C++ 11

6
Le langage C++
Contrôles à la compilation renforcés

• Le compilateur C++ est plus strict


– Le prototype d’une fonction doit être défini avant son utilisation.
– Erreur systématique générée en cas contraire.
– Pas de supposition sur le type du résultat et des paramètres.
– Contrôle des types des paramètres lors d’appels de fonctions.
– L’exécution d’une fonction doit toujours se terminer par un return.

Introduction à la programmation orientée objet avec C++ 12

Le langage C++
Exemple - compilation en C d’un programme incorrect

Type du paramètre incorrect

Fonction sans prototype

Aucune erreur, juste quelques


warnings

La compilation a réussi !

Introduction à la programmation orientée objet avec C++ 13

7
Le langage C++
Exemple - compilation en C++ d’un programme incorrect

Type du paramètre incorrect

Fonction sans prototype

En C++, erreurs de
compilation générées.

Introduction à la programmation orientée objet avec C++ 14

Le langage C++
Surcharge de fonctions

• Plusieurs fonctions de même nom peuvent être définies


– Elles doivent avoir une signature différente
– La signature est constituée de la liste des paramètres.
• Exemple
int minimum(int a, int b)
{
return a < b ? a : b;
}

int minimum(int a, int b, int c)


{
return minimum(a, minimum(b, c));
}

Introduction à la programmation orientée objet avec C++ 15

8
Le langage C++
Encodage de la signature des fonctions

• A l’issue de la compilation
– Le code objet contient plusieurs fonctions minimum.
– Comment le lieur peut il retrouver la bonne ?
• Dans le code généré par le C++
– Les noms de fonctions sont suffixés par un encodage de la signature.
– 2 fonctions de signature différente ont ainsi un nom différent.

Introduction à la programmation orientée objet avec C++ 16

Le langage C++
Encodage de la signature des fonctions

Fonction non trouvée :


"int __cdecl minimum(int,int)"
(?minimum@@YAHHH@Z)

Introduction à la programmation orientée objet avec C++ 17

9
Le langage C++
Mélanger des fichiers sources C et C++
• Problème lors du mélange de fichiers C et C++
Lib.h Lib.c

#ifndef __LIB_H_ #include "lib.h"

int minimum(int a, int b); int minimum(int a, int b)


{
#endif return a < b ? a : b;
}

Main.cpp

#include "lib.h"

int main()
{
int a;
a = minimum(1, 2);
return 0;
}

Introduction à la programmation orientée objet avec C++ 18

Le langage C++
Mélanger des fichiers sources C et C++

Main.cpp

#include "lib.h" Lib.c

int main() #include "lib.h"


{
int a; int minimum(int a, int b)
a = minimum(1, 2); {
return 0; return a < b ? a : b;
} }

Main.obj Lib.obj

_main, utilise ?minimum@@YAHHH@Z _minimum

Liaison impossible
Introduction à la programmation orientée objet avec C++ 19

10
Le langage C++
Mélanger des fichiers sources C et C++

• Solution
– Il faut indiquer au compilateur C++ quelles fonctions utilisent les
conventions de compilation du C plutôt que C++.
– Syntaxe
extern "C"
{
// déclarations à intepréter comme du C
}

• Exemple Main.cpp

extern "C"
{
#include "lib.h"
}

int main()
{
int a;
a = minimum(1, 2);
return 0;
}
Introduction à la programmation orientée objet avec C++ 20

Le langage C++
Valeurs par défaut des paramètres de fonctions

• Valeurs par défaut des paramètres de fonction


– Ce sont des valeurs utilisées pour un paramètre s’il n’est pas
donné au moment de l’appel de la fonction.
• C++ permet de définir des valeurs par défaut
– Lorsqu’un paramètre a une valeur par défaut, tous les paramètres
qui suivent doivent également en avoir une.
– Si le paramètre est fourni au moment de l’appel, c’est la valeur
fournie qui est utilisée.
– Sinon, c’est la valeur par défaut.
– Une fonction avec des paramètres par défaut peut donc être
appelée avec un nombre variable de paramètres.

Introduction à la programmation orientée objet avec C++ 21

11
Le langage C++
Valeurs par défaut des paramètres de fonctions
#include <iostream>

using namespace std;

void afficher_valeur(double valeur, const char * unite = "")


{
cout << valeur << " " << unite << endl;
}

int main()
{
afficher_valeur(12.5, "m");
afficher_valeur(12.5);
return 0;
}

Introduction à la programmation orientée objet avec C++ 22

Le langage C++
Les types référence - Introduction

• Définition
– Un type référence est en fait un pointeur.
– Utilisable sans la syntaxe fastidieuse des pointeurs du C.
• Particularités
– Une variable de type référence DOIT être initialisée.
– L’initialisation indique quelle variable est référencée.
– Il n’est ensuite plus possible de modifier la référence.
• Syntaxe
type & nom_reference = variable_referencee;

Introduction à la programmation orientée objet avec C++ 23

12
Le langage C++
Les types référence - Introduction

• Exemple
double x = 0.0, y = 1.0;
// déclaration et initialisation de la référérence :
double & rx = x;

// affectation de la variable référencée


rx = y;
cout << "x :" << x << endl;

• Comportement
– A l’initialisation, la référence reçoit l’adresse d’une variable.
– Lors de toutes les affectations ultérieures, c’est la variable
référencée qui est modifiée.
– La référence désigne donc toujours la même variable pendant
toute sa durée de vie.
Introduction à la programmation orientée objet avec C++ 24

Le langage C++
Passage de paramètres par référence

• Une référence est en fait un pointeur


• Permet donc de réaliser le passage de paramètre par
adresse.
• Sans la syntaxe lourde des pointeurs.

• Avec les références, C++, offre une solution élégante


pour le passage de paramètres par variable.

Introduction à la programmation orientée objet avec C++ 25

13
Le langage C++
Passage de paramètres par référence
void echanger(int & a, int & b)
{
int temporaire;
temporaire = a;
a = b;
b = temporaire;
}

int main()
{
int i, j;

i = 1;
j = 2;
cout << "i: " << i << " ; j: " << j << endl;
echanger(i, j);
cout << "i: " << i << " ; j: " << j << endl;
}

Introduction à la programmation orientée objet avec C++ 26

Le langage C++
Résultat de fonction de type référence
• Une fonction peut retourner une référence (donc une adresse)
– On peut affecter la variable référencée retournée.
– On peut ainsi placer un appel de fonction à gauche d’une affectation !
• Exemple

double tableau[100];

double & element(int i)


{
return tableau[i];
}

int main()
{
element(5) = 123;
cout << element(5) << endl;
cin >> element(10);
cout << element(10) << endl;
}

Introduction à la programmation orientée objet avec C++ 27

14
Le langage C++
Gestion de fichiers au format texte - écriture
#include <fstream>

using namespace std;

int main()
{
// déclaration d'un objet flux pour le fichier
ofstream fichier;
int i;

// ouverture du fichier
fichier.open("carres.txt");

// écriture dans le fichier


fichier << "Les carres de 1 a 100" << endl;
for (i = 1 ; i <= 100; i++)
fichier << i << "^2 : " << i * i << endl;

// fermeture du fichier
fichier.close();
}

Introduction à la programmation orientée objet avec C++ 28

Le langage C++
Gestion de fichiers au format texte - écriture
#include <fstream>

using namespace std;

int main()
{
// déclaration d'un objet flux pour le fichier
ofstream fichier;
int i;

// ouverture du fichier
fichier.open("carres.txt");

// écriture dans le fichier


fichier << "Les carres de 1 a 100" << endl;
for (i = 1 ; i <= 100; i++)
fichier << i << "^2 : " << i * i << endl;

// fermeture du fichier
fichier.close();
}

Introduction à la programmation orientée objet avec C++ 29

15
Le langage C++
Gestion de fichiers au format texte - lecture
#include <iostream>
#include <fstream>
#include <string>
using namespace std;

int main()
{
// déclaration d'un objet flux pour le fichier
ifstream fichier;
string ligne;
int nombre_ligne = 0;
// ouverture du fichier
fichier.open("carres.txt");
while (!fichier.eof())
{
// lecture du fichier, vérification du succès
if (getline(fichier, ligne))
nombre_ligne ++;
}
// fermeture du fichier
fichier.close();
cout << "Le fichier comporte " << nombre_ligne << " lignes." << endl;
}

Introduction à la programmation orientée objet avec C++ 30

Le langage C++
Gestion de fichiers – open

• Prototype
void open(const char *_Filename,
ios_base::openmode _Mode;

• Openmode
– app : append, va à la fin avant chaque ajout.
– ate : ouvre le fichier et va à la fin 1 fois.
– binary, : ouvre le fichier en mode binaire.
– in : ouvre en lecture
– out : ouvre en écriture
– trunc : écrase le fichier à l’ouverture.

• Exemple
fstream fichier;
fichier.open ("test.bin", ios::out | ios::app | ios::binary);

Introduction à la programmation orientée objet avec C++ 31

16
Le langage C++
La gestion des erreurs exceptionnelles
int ListeDouble::Ajouter(double x)
{ int Filtre::Echantilloner(double amplitude)
int indice; {
if (nombre_elements < CAPACITE) if (liste.Ajouter(amplitude) != INDICE_INVALIDE)
{ return 1;
indice = nombre_elements; else
tableau[indice] = x; {
nombre_elements++; printf("Erreur");
} return 0;
else }
indice = INDICE_INVALIDE; }
return indice;
}
Le code produit pour gérer les
erreurs exceptionnelles
double FiltreMedian(double valeurs[], int nombre)
{ représente 50 %
int i;
Filtre f;
Beaucoup d’effort pour
for (i = 0; i < nombre_valeurs; i++) propager l’erreur « liste
if (!f.Echantilloner(valeurs[i]))
return AMPLITUDE_INVALIDE; pleine » vers les fonctions de
return f.CalculerValeurMediane(); niveau supérieur.
}

Introduction à la programmation orientée objet avec C++ 32

Le langage C++
Les exceptions

• Lors de la détections d’une erreur


– On « lève » une exception.
– Une exception est un signal logiciel, accompagné d’une donnée.
– La donnée peut être de n’importe quel type
• Souvent un objet, de classe (ou sous classe) « exception ».
• Le traitement en cours est interrompu.
• L’exception se propage à travers les fonctions appelantes.
– Sans avoir aucun code à écrire pour cela.
• Jusqu’à ce qu’elle soit capturée.
– Au moment de la capture, la donnée associée est récupérable.
• Si elle n’est pas traitée
– En général : terminaison de l’application.

Introduction à la programmation orientée objet avec C++ 33

17
Le langage C++
Les exceptions - Syntaxe
• Lever une exception
throw exception("Liste pleine");
throw 1.5; // pas très usuel
• Capturer une exception
try
{
// Placer ici le code pouvant générer une exception
// que l'on veut capturer
}
// blocs de capture : un a plusieurs selon les besoins
catch (exception & e)
{
cout << e.what() << endl;
}
catch (...) // ... signifie capturer tout
{
cout << "Exception de type inattendu\n" << endl;
throw; // redéclenchement de l'exception capturée
}
Introduction à la programmation orientée objet avec C++ 34

Le langage C++
La gestion des erreurs avec les exceptions
int ListeDouble::Ajouter(double x)
{ void Filtre::Echantilloner(double amplitude)
int indice; {
if (nombre_elements < CAPACITE) liste.Ajouter(amplitude);
{ }
indice = nombre_elements;
tableau[indice] = x;
nombre_elements++;
}
else
throw exception("Liste pleine");
return indice;
} Plus aucun code à écrire pour la
propagation des erreurs !
double FiltreMedian(double valeurs[], int nombre)
{ Programmation beaucoup plus
int i;
Filtre f;
efficace.
for (i = 0; i < nombre_valeurs; i++)
f.Echantilloner(valeurs[i]);
return f.CalculerValeurMediane();
}

Introduction à la programmation orientée objet avec C++ 35

18
Le langage C++
La gestion des erreurs avec les exceptions
• Traitement de l’exception
– Se fait seulement au niveau où l’on sait comment réagir.
– Exemple : dans main, afficher simplement un message d’erreur.
• Exemple simple

double acquisitions[100];
double valeur_filtree;
. . .

try
{
valeur_filtree = filtre_median(acquisitions, 100);
}
catch (exception & e)
{
cout << "Erreur durant le filtrage : " << e.what() << endl;
}

Introduction à la programmation orientée objet avec C++ 36

Le langage C++
La gestion des erreurs avec les exceptions

• Recommandation
– Utiliser la gestion par exception seulement pour les situations
exceptionnelles.
– Préférer des tests avec if pour les situations probables.

Introduction à la programmation orientée objet avec C++ 37

19
Le langage C++
Allocation dynamique de mémoire

• C++ offre un opérateur pour l’allocation dynamique


– Opérateur new.
– Syntaxe et utilisation beaucoup plus clairs que les malloc du C.
• Syntaxe
TYPE * variable;
variable = new TYPE;
• Exemple – allocation d’une cellule pour une file chainée
struct CELLULE_FILE_CHAINEE * cellule;

cellule = new CELLULE_FILE_CHAINEE;


cellule->element = 10.5;

Introduction à la programmation orientée objet avec C++ 38

Le langage C++
Allocation dynamique de mémoire

• C++ offre également un opérateur pour la libération


– Opérateur delete.
• Syntaxe
TYPE * variable;
variable = new TYPE;
. . .
delete variable;

Introduction à la programmation orientée objet avec C++ 39

20
Le langage C++
Allocation dynamique de mémoire – les tableaux

• L’opérateur new permet d’allouer un tableau d’éléments


tableau = new type[taille];

• Syntaxe de l’opérateur delete pour les tableaux


delete []tableau;

• Allocation dynamique du C++


– Plus facile à utiliser.
– Plus lisible.
– A préférer pour tout développement en C++.
– A utiliser impérativement dès qu’on alloue des objets.

Introduction à la programmation orientée objet avec C++ 40

Le langage C++
Allocation dynamique de mémoire – les tableaux - exemple
int main()
{
double * tableau;
int i, taille;

cout << "Taille:";


cin >> taille;
// Allocation dynamique du tableau
tableau = new double[taille];
for (i = 0; i < taille; i++)
{
cout << "Element " << i + 1 << " : ";
cin >> tableau[i];
}
// Libération du tableau
delete []tableau;
_getch();
return 0;
}

Introduction à la programmation orientée objet avec C++ 41

21
Introduction à la programmation orientée objet avec C++ 42

Programmation orientée objet en C++


Introduction : Rappel – le TDA Nombre complexe en C
typedef struct
{
double reel, imaginaire;
} COMPLEXE;

void complexe_initialiser(COMPLEXE* z);

void complexe_ecrire_cartesien(COMPLEXE* z, double re, double im);


void complexe_ecrire_polaire(COMPLEXE* z, double module, double argument);

double complexe_lire_reelle(COMPLEXE z);


double complexe_lire_imaginaire(COMPLEXE z);
double complexe_lire_module(COMPLEXE z);
double complexe_lire_argument(COMPLEXE z);

COMPLEXE complexe_oppose(COMPLEXE z);


COMPLEXE complexe_conjugue(COMPLEXE z);

COMPLEXE complexe_somme(COMPLEXE z1, COMPLEXE z2);

Introduction à la programmation orientée objet avec C++ 43

22
Les types de donnée abstraits
Exemple d’utilisation correcte
#include "complexes.h"

int main()
{
COMPLEXE z1, z2, z3;

complexe_initialiser(&z1);
complexe_initialiser(&z2);
complexe_ecrire_cartesien(&z1, 3, 0);
complexe_ecrire_cartesien(&z2, 0, 2);
z3 = complexe_somme(z2, z1);
printf("z3 = %lf + i . %lf\n",
complexe_lire_reelle(z3),
complexe_lire_imaginaire(z3));
system("PAUSE");
return 0;
}

Introduction à la programmation orientée objet avec C++ 44

Les types de donnée abstraits


Exemple d’utilisation incorrecte
#include "complexes.h"

int main()
{
COMPLEXE z1 = {3.0, 0.0}, z2, z3; Attention. Mauvaise utilisation, car ne
respecte pas l’abstraction du type.

z2.reel = 0.0;
z2.imaginaire = 2.0;
z3 = complexe_somme(z2, z1);
printf("z3 = %lf + i . %lf\n",
complexe_lire_reelle(z3),
complexe_lire_imaginaire(z3));
system("PAUSE");
return 0;
}

Introduction à la programmation orientée objet avec C++ 45

23
Programmation orientée objet en C++
Introduction : Rappel – le TDA Nombre complexe en C - Analyse
void complexe_initialiser(COMPLEXE* z);

void complexe_ecrire_cartesien(COMPLEXE* z, double re, double im);


void complexe_ecrire_polaire(COMPLEXE* z, double module, double argument);

• Syntaxe fastidieuse
– Noms de méthodes préfixés par « complexe »
• pour rappeler le TDA d’appartenance
– Il faut toujours passer la structure en paramètre par adresse
• C++ : nouvelle syntaxe facilitant la programmation des TDA
– Des fonctions peuvent être déclarées dans un struct.
– On les appelle alors des « méthodes ».
– Ces méthodes ont accès directement aux variables de la struct.

Introduction à la programmation orientée objet avec C++ 46

Programmation orientée objet en C++


Les méthodes : des fonctions associées à la structure - déclaration
struct Complexe
{
double reelle, imaginaire;

void DefinirCartesien(double re, double im);


void DefinirPolaire(double module, double argument);

double LireReelle();
double LireImaginaire();
double LireModule();
double LireArgument();

Complexe Oppose();
Complexe Conjugue();

};

Introduction à la programmation orientée objet avec C++ 47

24
Programmation orientée objet en C++
Les méthodes : des fonctions associées à la structure - implémentation
void Complexe::DefinirCartesien(double re, double im)
{
reelle = re;
imaginaire = im;
} Dans l’implémentation, les
méthodes sont préfixées par le
double Complexe::LireReelle() nom du type.
{
return reelle;
} Tous les champs de la structure
sont accessibles directement
double Complexe::LireModule() depuis les méthodes.
{
return sqrt(reelle * reelle + imaginaire * imaginaire);
}

Complexe Complexe::Conjugue()
{
Complexe resultat;
resultat.DefinirCartesien(reelle, -imaginaire);
return resultat;
}

Introduction à la programmation orientée objet avec C++ 48

Programmation orientée objet en C++


Les méthodes : des fonctions associées à la structure - utilisation
#include <iostream>
#include "complexe.h"
Syntaxe allégée. Le mot struct
using namespace std; n’a pas besoin d’être répété.

int main()
{ On utilise le point pour accéder
Complexe z1, z2; aux méthodes (comme pour les
champs)
z1.DefinirCartesien(1.5, 2.0);
z2 = z1.Conjugue();

cout << "z1 : " << z1.LireReelle() << " + i . " <<
z1.LireImaginaire() << endl;
cout << "z2 : " << z2.LireReelle() << " + i . " <<
z2.LireImaginaire() << endl;

Introduction à la programmation orientée objet avec C++ 49

25
Programmation orientée objet en C++
Les méthodes : accès aux membres – comparaison C / C++

• En langage C
void complexe_definir_cartesien(COMPLEXE* z, double re, double im)
{
z->reel = re;
z->imaginaire = im; L’adresse de la structure est
évidemment requise pour
}
accéder aux champs.

• En langage C++
void Complexe::DefinirCartesien(double re, double im)
{
reelle = re;
imaginaire = im; L’adresse de la structure n’est
pas passée, et pourtant ça
} fonctionne !?

Introduction à la programmation orientée objet avec C++ 50

Programmation orientée objet en C++


Les méthodes : accès aux membres
• Le pointeur « this »
– En C++, les méthodes reçoivent un paramètre invisible.
– Il s’appelle « this ».
– C’est un pointeur sur la structure sur laquelle la méthode est appelée.
• Code écrit :
void Complexe::DefinirCartesien(double re, double im)
{
reelle = re;
imaginaire = im;
}
• De façon interne et silencieuse, le code vraiment compilé est :
void Complexe::DefinirCartesien (Complexe * this, double re,
double im)
{
this->reelle = re;
this->imaginaire = im;
}

Introduction à la programmation orientée objet avec C++ 51

26
Programmation orientée objet en C++
Gérer la visibilité : public et private

• Rappel –TDA
– TDA : Type de Données Abstrait.
– On ne devrait jamais accéder directement aux champs de la
structure.
– Mais rien ne nous empêche de le faire !
• Mauvais exemple d’utilisation du TDA:
int main()
{
Complexe z1, z2;

z1.reelle = 1.5;
z1.imaginaire = 2.0;
z2 = z1.Conjugue(); Accès direct aux champs.
. . . .
} Rompt l’abstraction.

Introduction à la programmation orientée objet avec C++ 52

Programmation orientée objet en C++


Gérer la visibilité : public et private
• C++ apporte des améliorations pour les TDA
– Possibilité d’interdire l’accès aux membres par les utilisateurs d’un type.
– Syntaxe : modificateurs public et private
• Exemple :
struct Complexe L’accès direct à ces champs hors
{ des méthodes du type Complexe
private: sera interdit par le compilateur.
double reelle, imaginaire;

public:
void DefinirCartesien(double re, double im);
void DefinirPolaire(double module, double argument);

double LireReelle();
double LireImaginaire(); Le type complexe peut seulement
. . . être utilisé avec les méthodes
}; publiques prévues.

Garantit le respect de l’abstraction

Introduction à la programmation orientée objet avec C++ 53

27
Programmation orientée objet en C++
Notion de classe
• La programmation orientée objet
– Elle est basée sur la notion de classe. (explicitée plus loin)
• Comment déclarer une classe en C++ ?
– Il suffit d’utiliser le mot class à la place du mot struct.

class Complexe
{
private:
double reelle, imaginaire;

public:
void DefinirCartesien(double re, double im);
void DefinirPolaire(double module, double argument);

double LireReelle();
double LireImaginaire();
. . . .
};

Introduction à la programmation orientée objet avec C++ 54

Programmation orientée objet en C++


Différence entre class et struct

• Points communs
– class et struct permettent tous deux de déclarer des classes.
– Le code généré est 100 % identique.
• Différences
– Avec la déclaration struct
• Par défaut, tous les membres sont publics.
– Avec la déclaration class
• Par défaut, tous les membres sont privés.
• Lequel préférer ?
– En programmation orientée objet, on utilise habituellement class.
– Par défaut, tout ce qui n’est pas explicitement rendu public par
le programmeur est ainsi privé.

Introduction à la programmation orientée objet avec C++ 55

28
Programmation orientée objet en C++
Notion d’objet

• Définition
– Un objet est une instance d’une classe.
– C’est-à-dire une variable de ce type.
• Exemples
Complexe z1, z2;
Complexe * z3;
z3 = new Complexe;
– z1 et z2 sont des objets.
– z3 est un pointeur sur un objet (son adresse).

Introduction à la programmation orientée objet avec C++ 56

Programmation orientée objet en C++


C++ et compilation séparée

• Le fichier .h
– Contient habituellement les déclarations de classes, méthodes, …
• Le fichier .cpp
– Contient l’implémentation des méthodes.

Introduction à la programmation orientée objet avec C++ 57

29
Programmation orientée objet en C++
Surcharge des opérateurs

• Surcharge des opérateurs


– Permet de donner un sens nouveau aux opérateurs.
– Seulement pour les types définis par le programmeur.
• Exemple : surcharge de << pour le type « Complexe »
– Opérateur déclaré EN DEHORS de la classe.
ostream & operator<<(ostream & stream, const Complexe & z)
{
stream << z.LireReelle();
if (z.LireImaginaire() < 0.0)
stream << " - " << - z.LireImaginaire() << " . i";
else if (z.LireImaginaire() > 0.0)
stream << " + " << z.LireImaginaire() << " . i";
return stream;
}

Introduction à la programmation orientée objet avec C++ 58

Programmation orientée objet en C++


Méthodes const
• Problème
– « z » est un paramètre const.
– A-t-on le droit d’appeler une méthode sur un const ?
– Et si la méthode modifiait la constante ?
– Compilateur: refus des appels de méthodes normales sur des
objets const
• Solution : déclarer les méthodes const
class Complexe
{
. . . .
double LireReelle() const;
double LireImaginaire() const;
double LireModule() const;
double LireArgument() const;
. . . .
};
Introduction à la programmation orientée objet avec C++ 59

30
Programmation orientée objet en C++
Surcharge des opérateurs - utilisation
#include <iostream>
#include "complexe.h"

using namespace std;

int main()
{
Complexe z1, z2;

z1.DefinirCartesien(1.5, 2.0);
z2 = z1.Conjugue(); Utilisation du nouvel opérateur
<<
cout << "z1 : ";
cout << z1 << endl;
cout << "z2 : ";
cout << z2 << endl;

char c;
cin >> c;
}

Introduction à la programmation orientée objet avec C++ 60

Programmation orientée objet en C++


Surcharge des opérateurs – l’addition de complexes

• Déclaration d’un opérateur dans la classe


class Complexe
{
public:
. . . .
Complexe operator+(const Complexe & operande);
. . . .
};

• Implémentation
Complexe Complexe::operator+(const Complexe & operande)
{
Complexe resultat;
resultat.reelle = reelle + operande.reelle;
resultat.imaginaire = imaginaire +
operande.imaginaire;
return resultat;
}

Introduction à la programmation orientée objet avec C++ 61

31
Programmation orientée objet en C++
Surcharge des opérateurs – l’addition de complexes - utilisation
#include <iostream>
#include "complexe.h"

using namespace std;

int main()
{
Complexe z1, z2;

z1.DefinirCartesien(1.5, 2.0);
z2.DefinirCartesien(3.0, 4.0);

cout << "z1 : ";


cout << z1 << endl;
cout << "z2 : ";
cout << z2 << endl;
cout << "z1 + z2 : " << z1 + z2 << endl;
Style de programmation naturel
} grâce aux opérateurs définis.

Introduction à la programmation orientée objet avec C++ 62

Programmation orientée objet en C++


Usages pour les noms des accesseurs

• Usage général
– Le code source est écrit en langue anglaise
• Méthodes d’accès en lecture
– On utilise le terme « get » : obtenir
double GetReal() const();
double GetImaginary() const;
• Méthodes d’accès en écriture
– On utilise le terme « set » : définir
void SetReal(double value);
• Terminologie
– On parle de « getter » et de « setter »

Introduction à la programmation orientée objet avec C++ 63

32
Programmation orientée objet en C++
Initialisation et finalisation d’un TDA

• Principes d’utilisation des TDA


– Initialisation : préparer les variables internes avant l’utilisation.
– Utilisation normale.
– Finalisation : libérer les ressources allouées
• Exemple : libérer la mémoire allouée dynamiquement avec malloc.
– En C: initialisation et finalisation à appeler explicitement.
• Apport du C++
– Notion de constructeur
• Méthode d’initialisation d’un objet.
• Appelée automatiquement à la création de l’objet.
– Notion de destructeur
• Méthode de finalisation d’un objet.
• Appelée automatiquement à la destruction de l’objet.

Introduction à la programmation orientée objet avec C++ 64

Programmation orientée objet en C++


Constructeur - Introduction

• Exemple – objet non initialisé


• int main()
{
Complexe z1;

cout << "z1 : ";


cout << z1 << endl; // non initialisé !
}

Introduction à la programmation orientée objet avec C++ 65

33
Programmation orientée objet en C++
Constructeur – déclaration et définition

• Déclaration
class Complexe
{
private:
double reelle, imaginaire; Méthode déclarée:
- Avec le nom de la classe.
public:
- Sans aucun type de résultat.
Complexe();
. . .
};
• Définition
Complexe::Complexe()
{
reelle = 0.0;
imaginaire = 0.0;
}

Introduction à la programmation orientée objet avec C++ 66

Programmation orientée objet en C++


Constructeur

• Exemple – objet initialisé automatiquement


int main()
{
Complexe z1; // Initialisé par le constructeur !

cout << "z1 : ";


cout << z1 << endl;
}

Introduction à la programmation orientée objet avec C++ 67

34
Programmation orientée objet en C++
Constructeur paramétré

• Déclaration
class Complexe
{
public:
Complexe();
Complexe(double re, double im);
. . .
};
• Définition
Complexe::Complexe(double re, double im)
{
DefinirCartesien(re, im);
}

Introduction à la programmation orientée objet avec C++ 68

Programmation orientée objet en C++


Constructeur

• Exemple – objet initialisé avec des paramètres


int main()
{
Complexe z1(1.0, 2.5);

cout << "z1 : ";


cout << z1 << endl;
}

Introduction à la programmation orientée objet avec C++ 69

35
Programmation orientée objet en C++
Objets membres et constructeur

• On construit une classe « SimulationImpedance »


• En utilisant un objet « Complexe » pour l’impédance

class SimulationImpedance
{
private:
Complexe impedance;
double frequence;
public:
void DefinirFrequence(double f);
void DefinirImpedance(const Complexe & z);
};

Introduction à la programmation orientée objet avec C++ 70

Programmation orientée objet en C++


Objets membres et constructeur

• Utilisation
int main()
{
SimulationImpedance s1, s2;

}
• Que se passe-t-il à la construction de s1, s2 ?
– Construction des objets interne.
– Appel automatique du constructeur de « Complexe ».

Introduction à la programmation orientée objet avec C++ 71

36
Programmation orientée objet en C++
Une liste tableau simple en C++
• Exemple : liste de double

#define CAPACITE 100

class ListeDouble
{
private:
double * tableau;
int nombre_elements;
public:
ListeDouble();
~ListeDouble();
ListeDouble(const ListeDouble & liste);
ListeDouble & operator=(const ListeDouble & valeur);
int Ajouter(double x);
int LireNombreElement() const;
double LireElement(int indice) const;
double operator[](int indice) const;
};
Introduction à la programmation orientée objet avec C++ 72

Programmation orientée objet en C++


Une liste tableau simple en C++

• Constructeur par défaut


ListeDouble::ListeDouble()
{
nombre_elements = 0;
tableau = new double[CAPACITE];
}

• Libération de la mémoire
– La mémoire allouée dynamiquement doit être libérée.
– Solution en C++ : le destructeur
• Méthode appelée automatiquement lorsque l’objet disparait.

Introduction à la programmation orientée objet avec C++ 73

37
Programmation orientée objet en C++
Destructeur

• Déclaration
class ListeDouble
{
public: Destructeur:
- Nom de la classe, précédé de ~
ListeDouble(); - Sans aucun type de résultat.
~ListeDouble();
. . .
};
• Définition
ListeDouble::~ListeDouble()
{
delete []tableau;
}

Introduction à la programmation orientée objet avec C++ 74

Programmation orientée objet en C++


Constructeur et destructeur – illustration
int main()
{

ListeDouble liste1; // construction de liste1


ListeDouble liste2; // construction de liste2

// liste1 et liste2 sont utilisables


liste1.Ajouter(2);
liste1.Ajouter(7);
liste1.Ajouter(4);
liste2 = liste1;

// A la sortie du bloc ou de la fonction


// destruction automatique de liste2
// destruction automatique de liste1
}

Introduction à la programmation orientée objet avec C++ 75

38
Programmation orientée objet en C++
Une liste tableau simple en C++

• Utilisation
int main()
{
ListeDouble liste1, liste2;
liste1.Ajouter(3.0);
liste2 = liste1;
}

• Constat
– Objets très facile à utiliser.
– Construits et détruits automatiquement.
• Problème
// Que fait cette instruction ?
liste2 = liste1;
– Exception à la fin du programme !

Introduction à la programmation orientée objet avec C++ 76

Programmation orientée objet en C++


Méthodes par défaut

• En C++, toute classe a au minimum


– 1 Constructeur par défaut.
– 1 Constructeur copie.
• Appelé lors de l’initialisation d’un objet.
• Copie chaque champ.
– 1 Opérateur d’affectation.
• Appelé lors de l’affectation d’un objet.
• Copie chaque champ.
• Par défaut, ces méthodes sont générées automatiquement.

Introduction à la programmation orientée objet avec C++ 77

39
Programmation orientée objet en C++
Une liste tableau simple en C++ - initialisation incorrecte de l’objet

Avant liste2 = liste1;


2 8 7 2 4 7

3 5 4 1 0 9

liste2 liste1

tableau tableau
nombre_elements nombre_elements

Introduction à la programmation orientée objet avec C++ 78

Programmation orientée objet en C++


Une liste tableau simple en C++ - initialisation incorrecte de l’objet

Après liste2 = liste1;


2 8 7 2 4 7

3 5 4 1 0 9

liste2 liste1

tableau tableau
nombre_elements nombre_elements

Introduction à la programmation orientée objet avec C++ 79

40
Programmation orientée objet en C++
Une liste tableau simple en C++ - initialisation incorrecte de l’objet

• Conséquences
– Une modification ultérieure de liste1 modifie aussi liste2 !
– Lors de la destruction
• La destruction de liste2 libère le bloc de mémoire de liste1.
• Lors de la destruction de liste1, ce bloc est libéré une deuxième fois.
• D’où une erreur fatale.
• Comportement souhaitable
– Lors de l’affectation ou de l’initialisation de liste2, il faudrait
• Copier le contenu de liste1 dans le tableau de liste2.
• Copier le nombre d’éléments.
– Ce comportement peut être programmé :
• Initialisation : surcharge du constructeur copie.
• Affectation : surcharge de l’opérateur « = »

Introduction à la programmation orientée objet avec C++ 80

Programmation orientée objet en C++


Une liste tableau simple en C++ - affectation correcte de l’objet
• Déclaration
class ListeDouble
{
public:
ListeDouble & operator=(const ListeDouble & valeur);
. . . .
};

• Définition
ListeDouble & ListeDouble::operator=(const ListeDouble & liste)
{
int i;
// Copie des éléments du tableau
for (i = 0; i < liste.nombre_elements; i++)
tableau[i] = liste[i];
// Copie du nombre d’éléments
nombre_elements = liste.nombre_elements;
// l’affectation renvoie la valeur de gauche
// Requis pour permettre le chaînage des affectations.
return *this;
}

Introduction à la programmation orientée objet avec C++ 81

41
Programmation orientée objet en C++
Une liste tableau simple en C++ - initialisation correcte de l’objet
• Déclaration
class ListeDouble
{
public:
ListeDouble();
~ListeDouble();
ListeDouble(const ListeDouble & liste);
. . . .
};

• Définition
ListeDouble::ListeDouble(const ListeDouble & liste)
{
int i;
// Constructeur : création du tableau nécessaire
tableau = new double[CAPACITE];
// Copie des éléments utilisés
for (i = 0; i < liste.nombre_elements; i++)
tableau[i] = liste[i];
// Copie du nombre d’éléments
nombre_elements = liste.nombre_elements;
}
Introduction à la programmation orientée objet avec C++ 82

Introduction à la programmation orientée objet avec C++ 83

42
Programmation orientée objet en C++
Héritage - Introduction
• Développement d’un logiciel de simulation électrique
• Composants pris en compte sous forme de classes séparées :
– Resistance
– Inductance
– Condensateur
• Observation
– Nombreux points communs

Resistance Inductance Condensateur

- resistance: double - inductance: double - capacite: double

- nom: string - nom: string - nom: string

+ CalculerImpedance(double) : Complexe + CalculerImpedance(double) : Complexe + CalculerImpedance(double) : Complexe

+ DefinirNom(string) : void + DefinirNom(string) : void + DefinirNom(string) : void


+ LireNom() : string + LireNom() : string + LireNom() : string
+ AfficherCaracteristiques() : void + AfficherCaracteristiques() : void + AfficherCaracteristiques() : void

Introduction à la programmation orientée objet avec C++ 84

Programmation orientée objet en C++


Héritage - Introduction

• Possibilité orientée objet du C++


– Créer une classe de base contenant les éléments communs
– Créer chaque classe particulière en « héritant » des
caractéristiques communes de la classe de base
• Intérêt
– Eviter les répétitions de code.
– Approche hiérarchique des problèmes.

Introduction à la programmation orientée objet avec C++ 85

43
Programmation orientée objet en C++
Héritage - Introduction

• Nouvelle répartition – pas de répétition de code

Introduction à la programmation orientée objet avec C++ 86

Programmation orientée objet en C++


Héritage – Réalisation en C++
Condensateur hérite de
class ComposantPassif ComposantPassif :
{
private: -Il reçoit tous les attributs et
string nom; méthodes de cette classe.
public:
void DefinirNom(const string & valeur); - L’héritage étant public, tous
string LireNom() const; les éléments publics hérités de
void AfficherCaracteristiques() const; ComposantPassif seront publics
}; sur la classe Condensateur.

class Condensateur: public ComposantPassif


{
private:
double capacite;
public:
Complexe CalculerImpedance(double frequence) const;
void DefinirCapacite(double valeur);
double LireCapacite() const;
};

Introduction à la programmation orientée objet avec C++ 87

44
Programmation orientée objet en C++
Héritage – Réalisation en C++
// Classe composant passif

void ComposantPassif::DefinirNom(const string & valeur)


{
nom = valeur;
}

string ComposantPassif::LireNom() const


{
return nom;
}

void ComposantPassif::AfficherCaracteristiques() const


{
cout << "Nom : " << nom << endl;
}

Introduction à la programmation orientée objet avec C++ 88

Programmation orientée objet en C++


Héritage – Réalisation en C++
// Classe Condensateur

Complexe Condensateur::CalculerImpedance(double frequence) const


{
Complexe resultat;
resultat.DefinirCartesien(0, - 1.0 / (capacite * 2 * M_PI *
frequence));
return resultat;
}

void Condensateur::DefinirCapacite(double valeur)


{
capacite = valeur;
}

double Condensateur::LireCapacite() const


{
return capacite;
}

Introduction à la programmation orientée objet avec C++ 89

45
Programmation orientée objet en C++
Héritage – Réalisation en C++
int main()
{
ComposantPassif compo1;
Condensateur c1;

compo1.DefinirNom("Composant 1");
c1.DefinirNom("Condensateur 1");
c1.DefinirCapacite(4.7e-6); // 4.7 µF

compo1.AfficherCaracteristiques();
cout << endl;
c1.AfficherCaracteristiques(); Méthodes définies seulement
dans ComposantPassif.
cout << endl;
Disponibles dans Condensateur
pause(); par héritage public.
}

Introduction à la programmation orientée objet avec C++ 90

Programmation orientée objet en C++


Héritage – Réalisation en C++

• Résultat :

• Observation
– Condensateur a bien hérité des attributs et méthodes de
ComposantPassif
– Résultat incomplet :
• Pour un condensateur, il faudrait aussi afficher la capacité.
• Solution : redéfinir la méthode « AfficherCaracteristiques ».

Introduction à la programmation orientée objet avec C++ 91

46
Programmation orientée objet en C++
Héritage – Redéfinir une méthode pour enrichir le comportement

• Déclaration Redéclaration de la méthode


class Condensateur: public ComposantPassif AfficherCaracteristiques.
{
public:
. . .
void AfficherCaracteristiques() const;
Appel de la méthode de la
}; classe parent.

• Définition
void Condensateur::AfficherCaracteristiques() const
{
ComposantPassif::AfficherCaracteristiques();
cout << "Capacite : " << capacite * 1.0e6 << " uF" << endl;
}

• Résultat

Introduction à la programmation orientée objet avec C++ 92

Programmation orientée objet en C++


Héritage – Représentation en mémoire

ComposantPassif Condensateur

string nom string nom Organisation en


mémoire identique
double capacite

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++ 93

47
Programmation orientée objet en C++
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);

Introduction à la programmation orientée objet avec C++ 94

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;
void AfficherImpedance(const Condensateur & composant)
composant.AfficherCaracteristiques();
{
while (frequence <= 1.0e6)
double frequence = 1.0;
{
Complexe impedance;
impedance = composant.CalculerImpedance(frequence);
void AfficherImpedance(const Inductance & composant)
composant.AfficherCaracteristiques();
cout << frequence << "{ Hz : " << impedance << endl;
while (frequence <= 1.0e6)
frequence = frequence * 10;double frequence = 1.0;
{
} Complexe impedance;
impedance = composant.CalculerImpedance(frequence);
} composant.AfficherCaracteristiques();
cout << frequence << " Hz : " << impedance << endl;
while (frequence <= 1.0e6)
frequence = frequence * 10;
{
}
impedance = composant.CalculerImpedance(frequence);
}
cout << frequence << " Hz : " << impedance << endl;
frequence = frequence * 10;
}
}

Introduction à la programmation orientée objet avec C++ 95

48
Programmation orientée objet en C++
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();
}

Introduction à la programmation orientée objet avec C++ 96

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.
ComposantPassif
void AfficherImpedance(const ComposantPassif & composant)
- nom: string
{
+ DefinirNom(st ring) : void double frequence = 1.0;
+ LireNom() : string
+ AfficherCaracteristiques() : void Complexe impedance;
composant.AfficherCaracteristiques();
while (frequence <= 1.0e6)
{
Induc tance impedance = composant.CalculerImpedance(frequence);
- inductance : double cout << frequence << " Hz : " << impedance << endl;
+ CalculerImpedance(double) : Complexe frequence = frequence * 10;
}
}
• Problème
– « CalculerImpedance » n’est pas déclarée sur ComposantPassif !
– Donc, compilation impossible.
Introduction à la programmation orientée objet avec C++ 97

49
Programmation orientée objet en C++
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.
Introduction à la programmation orientée objet avec C++ 98

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)
int main()
{
{
impedance = composant.CalculerImpedance(frequence);
Condensateur c1;
coutl1;
Inductance << frequence << " Hz : " << impedance << endl;
frequence
Resistance r1; = frequence * 10;
}
}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++ 99

50
Programmation orientée objet en C++
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.

Introduction à la programmation orientée objet avec C++ 100

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++ 101

51
Programmation orientée objet en C++
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

Introduction à la programmation orientée objet avec C++ 102

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++ 103

52
Programmation orientée objet en C++
Les méthodes virtuelles et le polymorphisme

void AfficherImpedance(const ComposantPassif & composant)


{
double frequence = 1.0;
Complexe impedance; La méthode effectivement
composant.AfficherCaracteristiques(); appelée derrière cet appel
while (frequence <= 1.0e6) dépend du type effectif de
{ l’objet.
impedance = composant.CalculerImpedance(frequence);
cout << frequence << " Hz : " << impedance << endl; La fonction AfficherImpedance
frequence = frequence * 10; 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.

Introduction à la programmation orientée objet avec C++ 104

Programmation orientée objet en C++


Les méthodes virtuelles et le polymorphisme - Fonctionnement

• Une classe avec des class A


{
méthodes virtuelles public:
long _l;
– Comporte automatiquement void Test() { }
une VMT : Virtual Method };
Table class B
{
– C’est un tableau contenant public:
l’adresse de ses différentes long _l;
méthodes virtuelles virtual void Test() { }
};
• Un objet d’une classe avec int main()
méthodes virtuelles {
cout << "A:" << sizeof(A) << endl;
– Comporte automatiquement cout << "B:" << sizeof(B) << endl;
}
un pointeur sur la VMT de sa
classe (_vfptr, _vptr, _vmt)

Introduction à la programmation orientée objet avec C++ 105

53
Programmation orientée objet en C++
Les méthodes virtuelles et le polymorphisme - Fonctionnement

ComposantPassif
VMTComposantPassif* __vmt;

VMTComposant

Resistance Inductance
VMTComposantPassif* __vmt; VMTComposantPassif* __vmt;

VMTInductance

VMTResistance

Introduction à la programmation orientée objet avec C++ 106

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.

Introduction à la programmation orientée objet avec C++ 107

54
Programmation orientée objet en C++
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++ 109

55
Programmation orientée objet en C++
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 !

Introduction à la programmation orientée objet avec C++ 110

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++ 111

56
Programmation orientée objet en C++
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
Liste de
ComposantPassif *

Resistance Inductance
Condensateur
Resistance
Condensateur Resistance
Condensateur

Inductance Condensateur

Introduction à la programmation orientée objet avec C++ 112

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 Induc tance Condensateur


- resistance : double - inductance : double - capacite: double

+ CalculerImpedance(double) : Complexe + CalculerImpedance(double) : Complexe + CalculerImpedance(double) : Complexe

Introduction à la programmation orientée objet avec C++ 113

57
Programmation orientée objet en C++
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.
Composant passif

ComposantPassif

- nom: string
Résistance
+ DefinirNom(st ring) : void
+ LireNom() : string
+ AfficherCaracteristiques() : void

Inductance

Resis tance Induc tance Condensateur


Condensateur
- resistance : double - inductance : double - capacite: double

+ CalculerImpedance(double) : Complexe + CalculerImpedance(double) : Complexe + CalculerImpedance(double) : Complexe

Introduction à la programmation orientée objet avec C++ 114

Programmation orientée objet en C++


Principes de conception logicielle orientée objet - UML

• Conception orientée objet : EtreV iv ant

– Découpage d’un problème


complexe en familles d’objets.
Végé tal Ani mal

– Notion d’objet : identité, état,


comportement.
– Notion assez intuitive.
Vertébrés Inv er tébré

• Caractéristique des logiciels


conçus selon une approche
Orientée Objet Mamifères

– Résistent assez bien aux


changements de spécifications.
– Aisément extensibles. Hum ain

– Meilleure pérennité.
Introduction à la programmation orientée objet avec C++ 115

58
Le langage C++
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>

Introduction à la programmation orientée objet avec C++ 116

Le langage C++
Concepts avancés – la généricité – déclaration d’une classe générique

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;
};

• 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
Introduction à la programmation orientée objet avec C++ 117

59
Le langage C++
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.

Introduction à la programmation orientée objet avec C++ 118

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++ 119

60
Le langage C++
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.

Introduction à la programmation orientée objet avec C++ 120

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++ 121

61
Le langage C++
Concepts avancés – la stl – aperçu du contenu

Introduction à la programmation orientée objet avec C++ 122

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++ 123

62
Qu’avons-nous appris ?
• Les bases du C++
– La compilation plus stricte.
– Les entrées sorties.
– Les espaces de nom.
– Les types références.
– Les nouveaux types de données.
– L’allocation dynamique.
– Les exceptions
• L’orientation objet
– Notions de classe, d’objet, de méthode.
– Surcharge d’opérateurs.
– Constructeurs, destructeurs.
– Méthodes virtuelles et polymorphisme.
– Concepts avancés: la généricité, la stl.
Introduction à la programmation orientée objet avec C++ 124

Vos questions

Introduction à la programmation orientée objet avec C++ 125

63
Introduction à la programmation orientée objet avec C++ 126

64

Vous aimerez peut-être aussi