• Aucun résultat trouvé

Programmation objet

N/A
N/A
Protected

Academic year: 2022

Partager "Programmation objet"

Copied!
20
0
0

Texte intégral

(1)

1 Département Informatique

Département Informatique

Les classes assistantes

Programmation objet

Cours n°4

(2)

Département Informatique Département Informatique

Classes assistantes

Introduction

Une classe (un objet) représente souvent un concept, une entité du programme.

Fréquemment, un de ces objets a besoin d'une entité extérieure, qui peut être une simple fonction (si le langage le permet),

mais qui souvent doit être représenté par une classe.

De telles classes, n'existant que pour être utilisées par un autre objet, sont nommées classes assistantes.

Elles sont souvent déclarées à l'intérieur de la classe assistée.

(3)

3 Département Informatique

Département Informatique

Exemple de classe assistante : les itérateurs

Dans un objet conteneur (par ex. la liste de la STL), il est souvent nécessaire de fournir un moyen pour accéder aux éléments, parcourir le conteneur, etc...

Un simple pointeur est souvent inefficace (risqué, bas niveau).

Une possibilité est de faire appel à une classe itérateur pour assister la collection.

Cette classe va encapsuler le code, parfois complexe, nécessaire pour parcourir la collection et accéder aux éléments.

(4)

Département Informatique Département Informatique

template <class T> class vecteur {

private:

T *p_tab;

int nb_elt;

public:

vecteur(int _taille):nb_elt(_taille) { p_tab=new T[nb_elt];

}

~vecteur()

{delete[] p_tab;}

T& operator[] (int _indice) {

return p_tab[_indice];

attributs :

adresse du tableau nombre d'éléments

(5)

5 Département Informatique

Département Informatique class iterator {

private:

int Indice;

vecteur& parent;

public:

iterator(int i=0, vecteur& v):Indice(i):parent(v){}

iterator& operator++() {

if(Indice<parent.size()) ++Indice;

return *this;

}

iterator& operator--(){...}

bool operator== const (const iterator&){...}

bool operator!= const (const iterator&){...}

T& operator*(){return parent[Indice];}

};

classe assistante interne

encapsulation de l'indice

permet une avancée sécurisée dans

la collection accès à la valeur

contenue dans la collection

(6)

Département Informatique Département Informatique

iterator begin() {

return iterator(0, *this);

}

iterator end() {

return iterator(nb_elt, *this);

}

iterator find(const T& _val){...}

};

Les méthodes de iterator se contentent d'encapsuler la notion réelle utilisée ici (un simple indice) tout en vérifiant un éventuel dépassement.

renvoie un itérateur sur le 1er élément

renvoie un itérateur sur un élément fictif

suivant le dernier élément de la collection

(7)

7 Département Informatique

Département Informatique

Exemple de classe assistante : pointeur intelligent

Dans de nombreux objets, on doit utiliser un pointeur au lieu d'une valeur : utilisation du polymorphisme, allocation dynamique, etc...

Mais dans ce cas, la responsabilité de la libération mémoire incombe au programme : risques de fuites mémoire, libération trop précoce...

Or le principe est toujours le même dans ce cas : un objet alloue de la mémoire, en cas de recopie de celui-ci « passe la responsabilité » à un autre objet (une autre instance), le dernier objet présent en mémoire devant absolument libérer la mémoire.

On a donc tout interet à confier ce travail à une classe particulière : Un pointeur intelligent (smart pointer)

(8)

Département Informatique Département Informatique

La bibliothèque standard fournit un pointeur intelligent : la classe std::auto_ptr<>

Cette classe permet d'utiliser l'idiome RAII.

Elle se contente néanmoins de peu et ne permet pas la recopie : son opérateur = se contente d'un transfert de propriété.

Cette classe n'est pas adaptée à un certain nombre d'utilisations,

par exemple être contenu dans une collection (comme std::vector)

(9)

9 Département Informatique

Département Informatique

Présentation de notre classe de pointeur intelligent : Doit fournir une sémantique de valeur à un objet dynamique.

Doit pouvoir se comporter comme un « vrai » pointeur.

Doit pouvoir gérer le partage de l'objet (plusieurs propriétaires)

Doit gérer automatiquement la libération mémoire, au bon moment.

Doit permettre le polymorphisme dynamique.

Pour tout ceci, la classe devra donc contenir un pointeur et un compteur de références, regroupées dans une même sous-classe, privée pour éviter leur manipulation par l'utilisateur.

Ne doit pas dépendre du type de données pointées.

(10)

Département Informatique Département Informatique

Opérations fournies par la classe :

Construction à partir d'un « vrai » pointeur. La classe prend alors la « responsabilité » du pointeur : elle s'occupera de sa libération.

Accès à la valeur pointée : définition de l'opérateur * et de l'opérateur ->

Gestion de la recopie : l'objet ayant une sémantique de valeur, il

est amené à être recopié. Définition donc d'un constructeur par recopie et d'un opérateur = qui devront incrémenter le compteur de références.

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.

(11)

11 Département Informatique

Département Informatique

iut::shared_ptr

Classe de pointeur intelligent de ty pe "partagé"

Projet : Workspace

Auteur : A.G. - IUT DIJON - IQ

Version : 1 Créé le : 25/9/2006 Modif ié le : 25/9/2006

<< auxiliary >>

- shared_ptr_ref + ptr

+ count + Prend + Libere + Prend + Libere

<< metaclass >>

+ shared_ptr + ptr_ref

(12)

Département Informatique Département Informatique

3

xxxx

valeur T shared_ptr_ref<T>

xxxx

xxxx

xxxx

shared_ptr<T>

(13)

13 Département Informatique

Département Informatique Construction normale d'un shared_ptr

En entrée : un « vrai » pointeur sur T (alloué)

Action : création (allocation) d'un shared_ptr_ref avec 1 référence Construction normale d'un shared_ptr_ref Action : simple initialisation des champs

En entrée : pointeur sur T

(14)

Département Informatique

Département Informatique Copie d'un shared_ptr

En entrée : un autre shared_ptr

Action : libère une référence du shared_ptr_ref puis ajoute une référence au shared_ptr_ref contenu dans le paramètre, le recopie.

Libération d'un shared_ptr

Décrémente le compteur de référence. S'il est nul, détruit le pointeur.

(15)

15 Département Informatique

Département Informatique

Exemple de classe assistante : optimisation de calcul matriciel Imaginons l'utilisation d'une classe MATRICE avec la redéfinition des opérateurs classiques (+, -, etc...) pour les calculs matriciels.

Les opérateurs comme + posent un problème : de par leur nature, ils doivent renvoyer une valeur.

MATRICE operator+(const MATRICE& m1, const MATRICE& m2) {

MATRICE retour;

// calcul de retour return retour;

}

Il y a donc passage obligatoire vers une (au moins) valeur temporaire, avec recopie, etc...

(16)

Département Informatique Département Informatique

L'utilisation de références ne peut pas être faite ici, car on ne peut pas référencer une valeur temporaire...

Examinez ce code :

MATRICE m1, m2, m3;

m1.Saisir();

m2.Saisir();

m3 = m1 + m2;

Une quatrième matrice

est créée ici, dans l'opérateur d'addition, puis recopiée dans m3...

Il serait donc plus intéressant d'effectuer le calcul au moment de l'affectation...

Ici aussi, une classe assistante (appelée dans ce cas proxy) peut nous aider.

(17)

17 Département Informatique

Département Informatique

class ADDMAT {

MATRICE& m1;

MATRICE& m2;

public:

ADDMAT(MATRICE& _m1, MATRICE& _m2);

};

Cette classe contenant deux références, elle ne recopie aucune matrice et donc n'occupe pratiquement pas d'espace mémoire, même avec

de « grosses » matrices.

Nous pourrions libeller alors l'opérateur + différemment :

(18)

Département Informatique Département Informatique

ADDMAT operator+(const MATRICE& m1, const MATRICE& m2) {

return ADDMAT(m1,m2);

}

Celui-ci n'effectue plus de calculs, mais la valeur renvoyée, bien qu'elle aussi temporaire, se réduit à deux références...

sa recopie n'est pas coûteuse.

Modifions alors l'opérateur = de la matrice : MATRICE& operator=(const ADDMAT& _am);

L'opérateur reçoit en fait la référence des deux matrices à ajouter : il peut directement faire le calcul, sans aucun temporaire !

(19)

19 Département Informatique

Département Informatique

De cette manière, le code :

M3 = m1 + m2;

Correspondra en fait à :

M3.operator=( operator+(m1,m2) );

Et pourra effectuer l'addition sans aucun temporaire.

Attention : le code m1+m2+m3 ne pourra pas fonctionner ! On pourrait de même effectuer l'opération m3 = m1*m2 + m

Sans aucun temporaire, grâce à quelques classes assistantes simples, tout le calcul étant délocalisé dans l'opérateur =.

(20)

Département Informatique Département Informatique

Prochain cours :

Structures de données – partie 1 tableaux, listes chaînées piles, files d'attente

A la semaine prochaine !

Références

Documents relatifs

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

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

A présent le code peut s'utiliser pour afficher tous les éléments d'une liste d'entiers différents de -5, tous les éléments d'un tableau de chaînes de moins de trois

sinon si valeur dans feuille courante &lt; valeur à ajouter Prend branche gauche comme nouvelle racine Ajout. sinon si valeur dans feuille courante &gt; valeur à ajouter Prend

parenthèse ouvrante : on remarque que l'utilisation d'une pile peut simplifier la gestion des parenthèses pour la construction de l'arbre..... 15

• 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