• Aucun résultat trouvé

Programmation objet

N/A
N/A
Protected

Academic year: 2022

Partager "Programmation objet"

Copied!
15
0
0

Texte intégral

(1)

L'idiome RAII

Programmation objet

Cours n°3

(2)

2 Département Informatique

Département Informatique

Introduction

Il est fréquent, dans une portion de code, d'avoir besoin d'acquérir une ressource au début, puis de la libérer à la fin.

Exemples :

ouverture / fermeture d'un fichier

allocation / libération de mémoire dynamique pose / dépose d'un verrou

affichage d'une barre/fenêtre d'attente etc...

Or il peut toujours arriver que la fonction ne se termine pas comme prévu, qu'une exception ait lieu avant la fin.

Dans ce cas, la ressource ne sera pas libérée : risques de fuites

(3)

Examinons par exemple le code suivant :

void AfficheListeEtudiants(Database& db, ecran& e) {

db.Open();

e.Clear();

Dataset ds=

db.ExecSQL("select * from etudiants;");

while(!ds.eof())

{ e.PutLine(ds.Field("Nom"));

ds.Next();

}

db.Close();

Acquisition de la ressource

Risque d'exception

(4)

4 Département Informatique

Département Informatique

Ce type de code est courant, paraît correct et pourtant ce n'est pas le cas : il est sensible aux exceptions.

On peut imaginer bien sûr gérer l'exception :

void AfficheListeEtudiants(Database& db, ecran& e) { db.Open();

try {

e.Clear();

Dataset ds =

db.ExecSQL("select * from etudiants;");

while(!ds.eof())

{ e.PutLine(ds.Field("Nom"));

ds.Next();

}catch(...){}} db.Close();

} Exécuté quoi qu'il arrive

Pas de traitement

(5)

L'idiome RAII (Ressource Acquisition Is Initialisation) permet de gérer ce type de problèmes d'une manière simple, lisible, sécurisée, portable, efficace.

Le principe consiste à utiliser une propriété très intéressante :

Le constructeur d'un objet automatique est automatiquement appelé lors de sa déclaration.

Le destructeur d'un objet automatique est automatiquement appelé quand il sort de sa portée.

Il suffit donc d'écrire un objet effectuant l'acquisition de la ressource

dans son constructeur, et la libération de celle-ci dans son destructeur.

(6)

6 Département Informatique

Département Informatique

Reprenons l'exemple précédent :

class OpenDb {

Database& db;

public:

OpenDb(Database& base):db(base){db.Open();}

~OpenDb(){db.Close();}

};

void AfficheListeEtudiants(Database& db, ecran& e) {

OpenDb GereDatabase(db);

e.Clear();

Dataset ds =

db.ExecSQL("select * from etudiants;");

while(!ds.eof()) {

e.PutLine(ds.Field("Nom"));

ds.Next();

} }

Objet pour le RAII

Objet auto qui gère le db

Quoi qu'il arrive le destructeur de GereDatabase est appelé !

(7)

Un tel code est plus robuste, et surtout n'est plus sensible aux exceptions.

On dit que ce code est « exception-safe ».

Le même principe est applicable pour de l'allocation mémoire (new/delete), la gestion d'un fichier (open/close), d'une connexion réseau, etc...

On peut également s'en servir pour de la gestion d'erreurs, pour éviter des branchements à l'intérieur d'un code, etc...

Il peut également servir à poser un verrou libérable automatiquement.

(8)

8 Département Informatique

Département Informatique Exemple avec de l'allocation mémoire

Il arrive fréquemment que l'on ait besoin d'allouer un pointeur en début de fonction pour le libérer à la fin de celle-ci.

En fait dans ce cas, on pourrait considérer un objet automatique (statique) à la place du pointeur.

Mais dans certains cas ce n'est pas possible, il faut utiliser un objet dynamique (par ex : les classes de la VCL comme TButton, etc...)

Dans ces cas, on utilisera un objet encapsulant le pointeur, et gérant la ressource.

(9)

template <class T> class ptr_auto {

T* ptr;

public:

ptr_auto(T* p=NULL):ptr(p){}

~ptr_auto(){delete ptr;}

T& operator*() const {return *ptr;}

T* operator->() const {return ptr;}

const T* get() const {return ptr;}

};

Cet objet prendra un « vrai » pointeur en paramètre (supposé créé par

un opérateur new), peut s'utiliser comme un « vrai » pointeur (surcharge

(10)

10 Département Informatique

Département Informatique

Exemple d'utilisation avec des classes de la VCL :

void JPEG2BMP(char *szFileBmp, char *szFileJpg) {

ptr_auto<TJpegImage> JpgImage(new TjpegImage);

JpgImage->LoadFromFile(szFileJpg);

ptr_auto<TBitmap> BmpImage(new TBitmap);

BmpImage->Assign(JpgImage.get());

BmpImage->SaveToFile(szFileBmp);

}

allocations

Risques d'exception

Quoi qu'il arrive ici,

la mémoire sera libérée.

Pour avoir le « vrai » pointeur

(11)

La bibliothèque standard du C++ utilise beaucoup l'idiome RAII.

Par exemple : std::string, qui obligatoirement doit faire des allocations de mémoire (pour stocker les caractères) et donc libérer, quoi qu'il arrive, même en cas d'exception interne ou d'erreur.

On retrouve encore le RAII dans l'utilisation des flux de fichiers (fstream) : {

std::ofstream Fichier("test.txt");

FonctionQuiRisqueUneExecption();

Fichier<<"Ceci est un test d'ecriture fichier";

}

On sort de la portée de « Fichier », son destructeur est donc appelé

(12)

12 Département Informatique

Département Informatique Utilisation du RAII pour poser un verrou

class Verrou {

bool& bVerrou;

public:

Verrou(bool& b):bVerrou(b){bVerrou=true;}

~Verrou(){bVerrou=false;}

}

bool Fini=false;

{ Verrou LeVerrou(Fini);

FQuiRisqueUneExecptionEtQuiABesoinDuVerrou();

}

(13)

Il arrive fréquemment que l'on souhaite afficher une attente pendant un traitement un peu long.

Cette attente doit s'afficher en début de traitement et être « cachée » à la fin de celui-ci, même s'il est interrompu par une exception.

Nous allons encore une fois utiliser l'idiome RAII pour effectuer cette attente .

Dans l'exemple suivant nous utilisons une fenêtre VCL (donc héritée de TForm)

(14)

14 Département Informatique

Département Informatique

class Attendre

{ TForm* FenAttente;

public:

Attendre(TForm* f):FenAttente(f){f->Show();}

~Attendre(){FenAttente->Hide();}

};

void FonctionQuiPrendDuTemps() { Attendre Fen(fWait);

while(TraitementFini()) { TraitementLong();

} }

On suppose fWait créée

Même si une exception a eu lieu, fWait sera cachée

(15)

Prochain cours :

Les classes assistantes Pointeurs intelligents Classes « Proxys »

A la semaine prochaine !

Références

Documents relatifs

Concatène à la fin de la chaîne courante, la sous-chaîne de &#34;str&#34; obtenue à partir de l’indice &#34;index&#34; , et en prenant &#34;len&#34; caractères... La fonction

Dans un langage de programmation sans orientée objet (Pascal, C, … ), il est interdit d'utiliser le même nom pour deux sous-programmes différents. Les langages C#, C++, JAVA,

Définition : La ……… d’une pyramide est la droite qui passe par le sommet de la pyramide et qui est perpendiculaire à la base. Définition : Un cône de révolution est un solide

Plusieurs instances de cette classe devront référencer le même objet en mémoire : définition d'une sous-classe assistante, associant l'adresse réelle de l'objet et un compteur...

Une pile représente un ensemble de données où l'on ne peut accéder qu'au dernier élément entré, qui devient donc le premier à sortir. On les appelle parfois LIFO (last in

• Arbre de recherche suivant la valeur Facile à programmer Très rapide à chercher Espace mémoire moyen Aucune n'est préférable, le contexte. décidera de la

Accès aux membres Modes d’héritage Amitié et héritage Surcharge de méthodes..

Elle ne peut donc pas être appelée directement, mais doit être redéfinie dans. les