Contenu connexe
Similaire à Partie 10: Classes Génériques — Programmation orientée objet en C++ (20)
Partie 10: Classes Génériques — Programmation orientée objet en C++
- 2. Vue d'Ensemble
Notions de base
Types, variables, opérateurs
Contrôle d'exécution
Fonctions
Mémoire dynamique
Qualité du logiciel
Evolution du modèle objet
Objets et classes
Fonctions membres
Classes génériques
Héritage
Polymorphisme
Héritage multiple
Entrée/sortie
POO en C++: Classes Génériques 314 © 1997-2003 Fabio HERNANDEZ
- 3. Table des Matières
Motivation
Déclaration d'une classe générique
Utilisation
Implémentation des fonctions membres
Considérations particulières
Fonctions génériques
Contraintes
Coût vs. Bénéfice
Résumé
POO en C++: Classes Génériques 315 © 1997-2003 Fabio HERNANDEZ
- 4. Motivation
Nous avons étudié les facilités offertes par C++ pour créer des
classes permettant de définir des conteneurs d'objets d'un
même type
Un conteneur est un objet qui agit sur une collection de zéro ou
plusieurs objets d'une classe particulière
Les classes List et Queue et les tableaux sont des exemples
des classes conteneurs
Le type des objets contenus dans un objet de la classe List ou
de la classe Queue est float
Les services de ces classes (append, prepend, insert,
remove,...) ne dépendent pas du type des objets qu'elles
contiennent
POO en C++: Classes Génériques 316 © 1997-2003 Fabio HERNANDEZ
- 5. Motivation (suite)
Supposons que nous voulons créer une List d'objets de type
char
Une possible solution consiste à dupliquer le code déjà écrit
pour la classe List en faisant les modifications pour que les
objets contenus soient de type char et pas float
il faudrait aussi renommer les classes (FloatList, CharList,...)
cette solution pose des problèmes pour la maintenance du code
Une autre possible solution est d'utiliser les facilités de
définition de macros du pré-processeur pour générer le code
nécessaire sur demande du programmeur
problèmes de maintenance
particulièrement inélégant
POO en C++: Classes Génériques 317 © 1997-2003 Fabio HERNANDEZ
- 6. Motivation (suite)
C++ fournit un mécanisme permettant de définir des classes (et
des fonctions) génériques (paramétrables)
L'idée est d'écrire des "moules" permettant de construire
d'autres classes
particulièrement utile pour les conteneurs
le paramètre de la classe correspond au type des objets contenus
En C++ cette facilité est connue sous le nom de template
Nous allons étudier à nouveau la classe List et la modifier de
façon à ce qu'elle devienne générique
POO en C++: Classes Génériques 318 © 1997-2003 Fabio HERNANDEZ
- 7. Contrôle d'Avancement
Motivation
Déclaration d'une classe générique
Utilisation
Implémentation des fonctions membres
Considérations particulières
Fonctions génériques
Contraintes
Coût vs. Bénéfice
Résumé
POO en C++: Classes Génériques 319 © 1997-2003 Fabio HERNANDEZ
- 8. Déclaration
#if !defined(LIST_H_INCLUDED)
#define LIST_H_INCLUDED
Item est le
template <class Item> paramètre de
class List { la classe List
public:
// Constructors/Destructor
List();
List(int initialCapacity);
List(const List& aList);
~List();
// Modifiers
bool append(Item anItem);
bool prepend(Item anItem);
bool insert(Item anItem, int position);
bool remove(Item anItem);
POO en C++: Classes Génériques 320 © 1997-2003 Fabio HERNANDEZ
- 9. Déclaration (suite)
bool removeAll(Item anItem);
bool replace(int position, Item anItem);
bool replaceAll(Item item1, Item item2);
void clear();
// Selectors
int length() const;
int occurrences(Item anItem) const;
int position(Item anItem) const;
Item itemAt(int position) const;
Item first() const;
Item last() const;
bool isEqual(const List& aList) const;
bool isEmpty() const;
POO en C++: Classes Génériques 321 © 1997-2003 Fabio HERNANDEZ
- 10. Déclaration (suite)
private:
// Data members
Item* data_;
int capacity_;
int length_;
// Help functions
void resize();
};
#endif // LIST_H_INCLUDED
POO en C++: Classes Génériques 322 © 1997-2003 Fabio HERNANDEZ
- 11. Contrôle d'Avancement
Motivation
Déclaration d'une classe générique
Utilisation
Implémentation des fonctions membres
Considérations particulières
Fonctions génériques
Contraintes
Coût vs. Bénéfice
Résumé
POO en C++: Classes Génériques 323 © 1997-2003 Fabio HERNANDEZ
- 12. Utilisation
Pour utiliser (créer une instance d') une classe générique il faut
fournir les paramètres nécessaires
Fichier main.cpp
#include "List.h"
void main() { Création d’une
List<char> alphabet; instance de la classe
alphabet.append('z'); générique avec le type
alphabet.prepend('a'); primitif char comme
paramètre
if (alphabet.isEmpty())
...
cout << "Length = " << alphabet.length() << endl;
...
}
POO en C++: Classes Génériques 324 © 1997-2003 Fabio HERNANDEZ
- 13. Utilisation (suite)
De façon similaire pour les listes des autres types primitifs
List<float> hitList;
List<int> counterList;
...
On peut aussi créer des instances des classes génériques avec
comme paramètre d'autres classes
#include "Point.h"
#include "List.h"
List<Point> vertexList; // A List of Point objects
POO en C++: Classes Génériques 325 © 1997-2003 Fabio HERNANDEZ
- 14. Utilisation (suite)
Notez que les instances des classes génériques sont
complètement identifiées par le nom de la classe et ses
paramètres
List<int> counterList;
List<char> alphabet;
...
// COMPILATION ERROR: 'counterList' and
// 'alphabet' are two objets of a different class
if (counterList.isEqual(alphabet))
cout << "They are equal" << endl;
POO en C++: Classes Génériques 326 © 1997-2003 Fabio HERNANDEZ
- 15. Utilisation (suite)
Afin de faciliter l'écriture, on peut définir des synonymes
#include "List.h"
Définition d'un
typedef List<char> CharList; synonyme d'une
... classe
générique
CharList alphabet;
alphabet.clear();
for(char letter='a'; letter <= 'z'; ++letter)
alphabet.append(letter);
...
POO en C++: Classes Génériques 327 © 1997-2003 Fabio HERNANDEZ
- 16. Contrôle d'Avancement
Motivation
Déclaration d'une classe générique
Utilisation
Implémentation des fonctions membres
Considérations particulières
Fonctions génériques
Contraintes
Coût vs. Bénéfice
Résumé
POO en C++: Classes Génériques 328 © 1997-2003 Fabio HERNANDEZ
- 17. Implémentation
Spécificités syntaxiques liées à la "généricité"
Exemple
template<class Item>
Item List<Item>::first() const
{
assert(length_ > 0);
return data_[0];
}
template<class Item>
inline
bool List<Item>::isEmpty() const
{
return (length_ == 0) ? true : false;
}
POO en C++: Classes Génériques 329 © 1997-2003 Fabio HERNANDEZ
- 18. Implémentation (suite)
L'implémentation des fonctions membres doit être contenue
(directe ou indirectement) lorsque l'on inclut le fichier de
l'interface
Fichier List.h
#if !defined(LIST_H_INCLUDED)
#define LIST_H_INCLUDED
template <class Item>
class List {
public:
...
private:
...
};
POO en C++: Classes Génériques 330 © 1997-2003 Fabio HERNANDEZ
- 19. Implémentation (suite)
Fichier List.h (suite)
template<class Item>
Item List<Item>::first() const
{
...
}
template<class Item>
inline
bool List<Item>::isEmpty() const
{
...
}
...
#endif // LIST_H_INCLUDED
POO en C++: Classes Génériques 331 © 1997-2003 Fabio HERNANDEZ
- 20. Paramètres des Classes Génériques
Spécification de plusieurs paramètres
template <class T1, class T2, class T3>
class ComplicatedClass {
public:
...
private:
...
};
Création d'un objet de cette classe
ComplicatedClass<float, int, Point> complicatedObject;
POO en C++: Classes Génériques 332 © 1997-2003 Fabio HERNANDEZ
- 21. Paramètres des Classes Génériques (suite)
Les paramètres peuvent aussi être des expressions
template <class Type, int Size>
class Buffer {
...
};
Utilisation
Buffer<double,256> littleBuffer;
Buffer<char,1024*10> bigBuffer;
const int MaxSize = 100;
Buffer<bool,MaxSize> answerBuffer;
POO en C++: Classes Génériques 333 © 1997-2003 Fabio HERNANDEZ
- 22. Contrôle d'Avancement
Motivation
Déclaration d'une classe générique
Utilisation
Implémentation des fonctions membres
Considérations particulières
Fonctions génériques
Contraintes
Coût vs. Bénéfice
Résumé
POO en C++: Classes Génériques 334 © 1997-2003 Fabio HERNANDEZ
- 23. Considérations Particulières
Cette implémentation fonctionne-t-elle aussi bien pour les
objets complexes que pour les objets des types primitifs?
template <class Item>
bool List<Item>::append(Item anItem)
{
if (length_ == capacity_)
resize();
data_[length_] = anItem;
++length_;
return true;
}
POO en C++: Classes Génériques 335 © 1997-2003 Fabio HERNANDEZ
- 24. Considérations Particulières (suite)
Nous devrions plutôt la modifier comme
template <class Item>
bool List<Item>::append(const Item& anItem)
{
if (length_ == capacity_)
resize();
data_[length_] = anItem;
++length_;
return true;
}
De façon similaire pour les autres fonctions membres
POO en C++: Classes Génériques 336 © 1997-2003 Fabio HERNANDEZ
- 25. Contrôle d'Avancement
Motivation
Déclaration d'une classe générique
Utilisation
Implémentation des fonctions membres
Considérations particulières
Fonctions génériques
Contraintes
Coût vs. Bénéfice
Résumé
POO en C++: Classes Génériques 337 © 1997-2003 Fabio HERNANDEZ
- 26. Fonctions Génériques
Syntaxe similaire à celle des classes
template <class Type>
Type min(Type a, Type b)
{
return (a < b) ? a : b;
}
template <class Item>
ostream& operator<<(ostream& stream,
const List<Item>& aList)
{
...
return stream;
}
POO en C++: Classes Génériques 338 © 1997-2003 Fabio HERNANDEZ
- 27. Contrôle d'Avancement
Motivation
Déclaration d'une classe générique
Utilisation
Implémentation des fonctions membres
Considérations particulières
Fonctions génériques
Contraintes
Coût vs. Bénéfice
Résumé
POO en C++: Classes Génériques 339 © 1997-2003 Fabio HERNANDEZ
- 28. Contraintes
L'implémentation des fonctions membres d'une classe
générique impose des contraintes sur les classes utilisées
comme paramètre du template
Exemple
la fonction membre
int List<Item>::position(const Item& anItem) const
suppose que la classe Item fournit l'opérateur d'égalité
la fonction membre
int List<Item>::append(const Item& anItem)
suppose que la classe Item fournit l'opérateur d'affectation
L'instance ne pourra pas être créée si ces suppositions ne sont
pas satisfaites au moment de la compilation
POO en C++: Classes Génériques 340 © 1997-2003 Fabio HERNANDEZ
- 29. Contrôle d'Avancement
Motivation
Déclaration d'une classe générique
Utilisation
Implémentation des fonctions membres
Considérations particulières
Fonctions génériques
Contraintes
Coût vs. Bénéfice
Résumé
POO en C++: Classes Génériques 341 © 1997-2003 Fabio HERNANDEZ
- 30. Coût vs. Bénéfice
Avantages
mécanisme très puissant
Inconvénients
syntaxe dissuasive
pas encore (bien) supporté par quelques compilateurs
temps de compilation/édition de liens augmenté
taille de l'exécutable augmentée
POO en C++: Classes Génériques 342 © 1997-2003 Fabio HERNANDEZ
- 31. Résumé
Les classes génériques servent pour décrire des conteneurs
indépendamment du type des objets contenus
Le client de la classe générique doit fournir les types
nécessaires pour la création de l’instance de la classe générique
L'implémentation des classes génériques suppose une attention
particulière puisque le type de chaque paramètre n'est pas
connu à priori
Bien que normalisé, ce mécanisme n'est pas encore supporté par
tous les compilateurs
POO en C++: Classes Génériques 343 © 1997-2003 Fabio HERNANDEZ