• Aucun résultat trouvé

Programmation orientée objet avec C++

N/A
N/A
Protected

Academic year: 2022

Partager "Programmation orientée objet avec C++"

Copied!
64
0
0

Texte intégral

(1)

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.

(2)

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

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

Le langage C++

Créer un programme C++ avec Visual Studio

• Donner l’extension .cpp au fichier source !

(3)

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

Les entrées sorties standard du C++

#include <iostream>

int main() {

double x, y;

std::cout << "X:";

std::cin >> x;

std::cout << "Y:";

std::cin >> y;

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

return 0;

}

Bibliothèque d’entrées sorties du C++

Flux de sortie

Opérateur surchargé en C++.

Ecriture vers un flux.

Flux d’entrée.

Opérateur surchargé en C++.

Lecture depuis un flux.

Opérateur pouvant être chaîné

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;

(4)

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

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;

}

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

}

(5)

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

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;

}

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) . . .

(6)

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

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;

}

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;

}

(7)

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

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.

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 !

(8)

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

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.

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

}

(9)

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

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.

Le langage C++

Encodage de la signature des fonctions

Fonction non trouvée :

"int __cdecl minimum(int,int)"

(?minimum@@YAHHH@Z)

(10)

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

Mélanger des fichiers sources C et C++

• Problème lors du mélange de fichiers C et C++

Lib.h

#ifndef __LIB_H_

int minimum(int a, int b);

#endif

Lib.c

#include "lib.h"

int minimum(int a, int b) {

return a < b ? a : b;

}

Main.cpp

#include "lib.h"

int main() {

int a;

a = minimum(1, 2);

return 0;

}

Le langage C++

Mélanger des fichiers sources C et C++

Lib.c

#include "lib.h"

int minimum(int a, int b) {

return a < b ? a : b;

} Main.cpp

#include "lib.h"

int main() {

int a;

a = minimum(1, 2);

return 0;

}

Main.obj

_main, utilise ?minimum@@YAHHH@Z

Lib.obj _minimum

Liaison impossible

(11)

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

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;

}

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.

(12)

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

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;

}

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;

(13)

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

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.

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.

(14)

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

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;

}

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;

}

(15)

Gestion de fichiers au format texte - écriture

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

#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();

}

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();

}

(16)

Gestion de fichiers au format texte - lecture

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

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

}

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

(17)

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

La gestion des erreurs exceptionnelles

int ListeDouble::Ajouter(double x) {

int indice;

if (nombre_elements < CAPACITE) {

indice = nombre_elements;

tableau[indice] = x;

nombre_elements++;

} else

indice = INDICE_INVALIDE;

return indice;

}

int Filtre::Echantilloner(double amplitude) {

if (liste.Ajouter(amplitude) != INDICE_INVALIDE) return 1;

else {

printf("Erreur");

return 0;

} }

double FiltreMedian(double valeurs[], int nombre) {

int i;

Filtre f;

for (i = 0; i < nombre_valeurs; i++) if (!f.Echantilloner(valeurs[i])) return AMPLITUDE_INVALIDE;

return f.CalculerValeurMediane();

}

Le code produit pour gérer les erreurs exceptionnelles représente 50 % Beaucoup d’effort pour propager l’erreur « liste pleine » vers les fonctions de niveau supérieur.

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.

(18)

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

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 }

Le langage C++

La gestion des erreurs avec les exceptions

int ListeDouble::Ajouter(double x) {

int indice;

if (nombre_elements < CAPACITE) {

indice = nombre_elements;

tableau[indice] = x;

nombre_elements++;

} else

throw exception("Liste pleine");

return indice;

}

double FiltreMedian(double valeurs[], int nombre) {

int i;

Filtre f;

for (i = 0; i < nombre_valeurs; i++) f.Echantilloner(valeurs[i]);

return f.CalculerValeurMediane();

}

void Filtre::Echantilloner(double amplitude) {

liste.Ajouter(amplitude);

}

Plus aucun code à écrire pour la propagation des erreurs ! Programmation beaucoup plus efficace.

(19)

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

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;

}

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.

(20)

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

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;

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;

(21)

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

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.

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;

}

(22)

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

(23)

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

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;

}

Attention. Mauvaise utilisation, car ne respecte pas l’abstraction du type.

Les types de donnée abstraits

Exemple d’utilisation incorrecte

#include "complexes.h"

int main() {

COMPLEXE z1 = {3.0, 0.0}, z2, z3;

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;

}

(24)

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

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.

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();

};

(25)

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

Les méthodes : des fonctions associées à la structure - implémentation

void Complexe::DefinirCartesien(double re, double im) {

reelle = re;

imaginaire = im;

}

double Complexe::LireReelle() {

return reelle;

}

double Complexe::LireModule() {

return sqrt(reelle * reelle + imaginaire * imaginaire);

}

Complexe Complexe::Conjugue() {

Complexe resultat;

resultat.DefinirCartesien(reelle, -imaginaire);

return resultat;

}

Dans l’implémentation, les méthodes sont préfixées par le nom du type.

Tous les champs de la structure sont accessibles directement depuis les méthodes.

Programmation orientée objet en C++

Les méthodes : des fonctions associées à la structure - utilisation

#include <iostream>

#include "complexe.h"

using namespace std;

int main() {

Complexe z1, z2;

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;

}

On utilise le point pour accéder aux méthodes (comme pour les champs)

Syntaxe allégée. Le mot struct n’a pas besoin d’être répété.

(26)

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

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;

}

• En langage C++

void Complexe::DefinirCartesien(double re, double im) {

reelle = re;

imaginaire = im;

}

L’adresse de la structure est évidemment requise pour accéder aux champs.

L’adresse de la structure n’est pas passée, et pourtant ça fonctionne !?

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;

}

(27)

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

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.

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 {

private:

double reelle, imaginaire;

public:

void DefinirCartesien(double re, double im);

void DefinirPolaire(double module, double argument);

double LireReelle();

double LireImaginaire();

. . . };

L’accès direct à ces champs hors des méthodes du type Complexe sera interdit par le compilateur.

Le type complexe peut seulement être utilisé avec les méthodes publiques prévues.

Garantit le respect de l’abstraction

(28)

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

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();

. . . . };

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é.

(29)

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

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).

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.

(30)

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

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;

}

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;

. . . . };

(31)

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

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();

cout << "z1 : ";

cout << z1 << endl;

cout << "z2 : ";

cout << z2 << endl;

char c;

cin >> c;

}

Utilisation du nouvel opérateur

<<

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;

}

(32)

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

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.

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 »

(33)

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

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.

Programmation orientée objet en C++

Constructeur - Introduction

• Exemple – objet non initialisé

int main()

{

Complexe z1;

cout << "z1 : ";

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

(34)

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

Constructeur – déclaration et définition

• Déclaration

class Complexe {

private:

double reelle, imaginaire;

public:

Complexe();

. . . };

• Définition

Complexe::Complexe() {

reelle = 0.0;

imaginaire = 0.0;

}

Méthode déclarée:

- Avec le nom de la classe.

- Sans aucun type de résultat.

Programmation orientée objet en C++

Constructeur

• Exemple – objet initialisé automatiquement

int main() {

Complexe z1; // Initialisé par le constructeur !

cout << "z1 : ";

cout << z1 << endl;

}

(35)

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

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

}

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;

}

(36)

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

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

};

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 ».

(37)

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

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;

};

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.

(38)

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

Destructeur

• Déclaration

class ListeDouble {

public:

ListeDouble();

~ListeDouble();

. . . };

• Définition

ListeDouble::~ListeDouble() {

delete []tableau;

}

Destructeur:

- Nom de la classe, précédé de ~ - Sans aucun type de résultat.

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 }

(39)

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

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 !

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.

(40)

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

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

liste1 tableau nombre_elements

3 5 4 1 0 9 2 8 7 2 4 7

liste2 tableau nombre_elements

Avant liste2 = liste1;

Programmation orientée objet en C++

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

liste1 tableau nombre_elements liste2

tableau nombre_elements

Après liste2 = liste1;

3 5 4 1 0 9 2 8 7 2 4 7

(41)

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

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 « = »

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;

}

(42)

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

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;

}

(43)

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

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 - resistance: double - nom: string

+ CalculerImpedance(double) : Complexe + DefinirNom(string) : void + LireNom() : string + AfficherCaracteristiques() : void

Inductance - inductance: double - nom: string

+ CalculerImpedance(double) : Complexe + DefinirNom(string) : void + LireNom() : string + AfficherCaracteristiques() : void

Condensateur - capacite: double - nom: string

+ CalculerImpedance(double) : Complexe + DefinirNom(string) : void + LireNom() : string + AfficherCaracteristiques() : void

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.

(44)

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

Héritage - Introduction

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

Condensateur hérite de ComposantPassif :

-Il reçoit tous les attributs et méthodes de cette classe.

- L’héritage étant public, tous les éléments publics hérités de ComposantPassif seront publics sur la classe Condensateur.

Programmation orientée objet en C++

Héritage – Réalisation en C++

class ComposantPassif {

private:

string nom;

public:

void DefinirNom(const string & valeur);

string LireNom() const;

void AfficherCaracteristiques() const;

};

class Condensateur: public ComposantPassif {

private:

double capacite;

public:

Complexe CalculerImpedance(double frequence) const;

void DefinirCapacite(double valeur);

double LireCapacite() const;

};

(45)

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

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;

}

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;

}

(46)

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

Méthodes définies seulement dans ComposantPassif.

Disponibles dans Condensateur par héritage public.

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();

cout << endl;

pause();

}

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 ».

(47)

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

Appel de la méthode de la classe parent.

Redéclaration de la méthode AfficherCaracteristiques.

Héritage – Redéfinir une méthode pour enrichir le comportement

• Déclaration

class Condensateur: public ComposantPassif {

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

Références

Documents relatifs

Lors de l’évaluation d’une variable (dans le corps d’une fonction), si la variable est Locale, sa valeur est prise, sinon si la variable est dans une fonction Englobant, sa valeur

Si l'on appel notre méthode publique Animaux() depuis une instance de $zoo, cela va retourner de manière sécurisée notre tableau d'animaux stocké dans notre classe, et personne ne

une commande est créée pour un client et un catalogue donnés, on peut ajouter des articles à une commande, accéder à la liste des articles commandés ainsi que prix total des

Hors cette étape n'est clairement pas du ressort d'un objet : seule la classe possède suffisamment d'informations pour la mener à bien : la création d'un objet est donc une méthode

Les exemples de code sont écrits en C#, mais sont facilement transposables à d'autres langages orientés objets comme

L’interpréteur java permet d’exécuter une application écrite en langage java (autre qu’une applet), plus spécifiquement un fichier ClassName.class (i.e le java bytecodes). Par

Une interface est introduite par le mot clé « interface », et se comporte comme une classe dont toutes les méthodes sont abstract et dont tous les attributs sont final. Les mots

Additionner deux vecteurs donnés ; on écrira pour cela une méthode statique nommée aussi addition (en utilisant ainsi la possibilité de la surcharge) qui recevra en