Télécharger la présentation
La présentation est en train de télécharger. S'il vous plaît, attendez
1
Structures de données IFT-10541
Abder Alikacem Programmation orienté objet en C++ Module 2 Édition Septembre 2009 Département d’informatique et de génie logiciel
2
Plan Introduction au mondes des objets: les concepts
01/04/2017 Plan Introduction au mondes des objets: les concepts Le concept d'Objet Les principes de l’Approche Orientée Objet Les relations entre Objets Quelques définitions Principe de l’encapsulation Classes et objets Les modèles (templates) Programmation générique : introduction, exemples Déclaration des modèles Fonctions et méthodes génériques Instanciation Spécialisation Compilation séparée Traitement des exceptions Vice-rectorat à la recherche / octobre 2005
3
Le concept d’objet (1/3) Exemple : une personne, une voiture, une procédure, une loi, ... Objet : abstraction du monde réel entité possédant un état (information) offrant des services / opérations pour examiner / modifier son état (comportement) Modèle Orienté-Objet (OO) d’une application : définition d’un ensemble d’objets coopérants - communicants - et concurrents - vivants indépendamment les uns des autres
4
Le concept d’objet (2/3) Objet = identité + état + comportement
Exemple : la voiture 1875 WC 31 état : immatriculation, kilométrage, vitesse instantanée, état interne… services / opérations : accesseurs : kilométrage?, immatriculation? ... modificateurs : démarrer, s’arrêter, se déplacer, tomber en panne, être réparée ... Essentiel : un objet n'est manipulable qu'à travers son interface i.e. ses opérations publiques
5
Le concept d'objet (3/3) Tout objet a une durée de vie finie : il naît, vit et meurt ! Tout objet est en relation avec d'autres objets : relations statiques : (relativement) stables dans le temps relations dynamiques : brèves, limitées à la communication Tout objet peut changer de forme - sous certaines conditions - au cours de son existence : principe de polymorphisme
6
Principes sur lesquels reposent les Objets
Encapsulation : regroupement des informations d'état et de comportement sous un nom unique Masquage d'information : on ne connaît pas la structure de l'information Interface : seuls les services publics (offerts à l'extérieur) sont utilisables Envoi de messages : les objets communiquent par messages Durée de vie limitée : dépendante du contexte
7
Les relations entre Objets
Relations statiques : composition / agrégation : traduit l'assemblage généralisation / spécialisation : permet la réutilisation association n-aire : relation classique Relations dynamiques : communication : échange de données signal : échange de stimuli
8
Généralisation / spécialisation
Généralisation : a posteriori pour favoriser la réutilisation future Spécialisation : c'est de la réutilisation en extension : ajout de nouvelles propriétés en compréhension : modification de propriétés Conséquences : Polymorphisme : possibilité pour une instance de changer de classe tout en restant dans la même hiérarchie Héritage simple / multiple
9
Représentation d'objet
Son nom Son état Opération publique La voiture de Jean kilométrage? kilométrage : ... vitesse : ... immatriculation : ... démarrer Un objet tomber en panne ... Opération privée
10
Quelques définitions Classe = regroupement d'objets ayant des propriétés (état et/ou comportement et/ou relations) communes Classe = moule qui décrit la structure interne d'un ensemble d'objets Classe = abstraction qui n'a de sens qu'à la compilation Objet = un instance d'une classe Objet = instance qui n'a d'existence qu'à l'exécution
11
Propriétés des objets Unicité :
un objet n'est instance que d'une classe et d'une seule une instance a une identité (interne) unique Durée de vie : une instance doit être déclarée, puis définie en fonction du contexte. un objet vit sa vie puis meurt. la durée de vie peut être très courte, très grande ! Polymorphisme : un objet peut changer de forme au cours de sa vie ! Concurrence : les objets évoluent indépendamment les uns des autres
12
Conception Orientée Objet
Deux étapes fondamentalement différentes : Identification des classes à réutiliser ou à concevoir : utilisation d’une méthode OO pour la conception et la définition des hiérarchies de classes à utiliser ou à développer Construction de l’Application OO : transformation de la conception de l’application en un ensemble d’objets concurrents et coopérants
13
Intérêt des Objets Maîtrise de la complexité :
l'encapsulation permet de se concentrer sur un(e) objet(classe) et un(e) seul(e) il est possible de tester de façon quasi exhaustive un objet Evolution facilitée : rajouter de nouveaux objets est simplifié. On les raccroche aux modèles déjà construits par spécialisation Réutilisation : vraiment possible à cause des classifications induites par la relation de spécialisation / généralisation ! ...
14
Inconvénients de l'approche OO
Tout n'est pas objet : se méfier des chapelles et des gourous ! Tester un ensemble un système OO est difficile ! Penser objet oblige à changer de mentalité !
15
Encapsulation, polymorphisme et héritage
01/04/2017 Encapsulation, polymorphisme et héritage Encapsulation Le rassemblement des données et du code les utilisant dans une entité unique (objet). La séparation nette entre la partie publique d'un objet (ou interface) seule connue de l'utilisateur de la partie privée ou implémentation qui reste masquée. Polymorphisme Une méthode peut adopter plusieurs formes différentes. Héritage Possibilité de définir des familles de classes traduisant le principe de généralisation / spécialisation. « La classe dérivée est une version spécialisée de sa classe de base » Vice-rectorat à la recherche / octobre 2005
16
Encapsulation et abstraction
01/04/2017 Encapsulation et abstraction L'encapsulation Consiste à masquer l'accès à certains attributs et méthodes d'une classe. Pourquoi masquer? Cacher les détails d'implémentation des objets à l'utilisateur permet de modifier, par exemple la structure de données interne d'une classe (remplacer un tableau par une liste chaînée) sans pour autant entraîner de modifications dans le code de l’utilisateur, l’interface n’étant pas atteinte. Abstraction de données La structure d'un objet n'est pas visible de l'extérieur, son interface est constituée de messages invocables par un utilisateur. La réception d'un message déclenche l'exécution de la méthode correspondant à ce message. Abstraction procédurale Du point de vue de l'extérieur (c’est-à-dire en fait du client de l’objet), l'invocation d'un message est une opération atomique. L'utilisateur n'a aucun élément d'information sur la mécanique interne mise en œuvre. Par exemple, il ne sait pas si le traitement requis a demandé l’intervention de plusieurs méthodes ou même la création d’objets temporaires etc. Vice-rectorat à la recherche / octobre 2005
17
01/04/2017 Droits d’accès En C++, on choisit les paramètres d’accès aux membres d’une encapsulation à l'aide des mots clés : private : les membres privés ne sont accessibles que par les fonctions membres de la classe. protected : les membres protégés sont comme les membres privés. Mais ils sont aussi accessibles par les fonctions membres des classes dérivées. public : les membres publics sont accessibles par tous. La partie publique est appelée interface. Les mots réservés private , protected et public peuvent figurer plusieurs fois dans la déclaration de la classe. Le droit d'accès ne change pas tant qu'un nouveau droit n'est pas spécifié. Vice-rectorat à la recherche / octobre 2005
18
Les différents type d’encapsulation
01/04/2017 Les différents type d’encapsulation Il existe différents types d’encapsulation : struct Classe1 { /* ... */ }; tous les membres sont par défaut d'accès public par défaut. union Classe2 { /* ... */ }; tous les membres sont d'accès public par défaut. class Classe3 { /* ... */ }; tous les membres sont d'accès private par défaut. C'est cette dernière forme qui est utilisée en C++ pour définir des classes. Vice-rectorat à la recherche / octobre 2005
19
Principe de la conception orienté objet
01/04/2017 Principe de la conception orienté objet Algorithmes + Structures de données = Programme Approche traditionnelle Organiser autour du traitement Fonction 1 Fonction 2 Structures de données Fonction 3 Fonction n Vice-rectorat à la recherche / octobre 2005
20
Principe de la conception orienté objet
01/04/2017 Principe de la conception orienté objet Organiser autour des données à manipuler Données Fonctions de manipulations et de traitements Objet Vice-rectorat à la recherche / octobre 2005
21
Objet en C++ Programme C++ Programme C Class Date { typedef struct {
01/04/2017 Objet en C++ Programme C++ Programme C typedef struct { int jour; int mois; int année; } Date; void initData(Data d,int j,int m,int a){ d.jour=j; d.mois=m; d.année=a; } int main(){ Date d; initData(d,2,2,2004); return 0; Class Date { public : void initData(int j,int m,int a); protected: int _jour; int _mois; int _année; }; Date::initData(int j,int m,int a){ _jour=j; _mois=m; _année=a; } int main(){ Date d; d.initData(2,2,2004); return 0; Vice-rectorat à la recherche / octobre 2005
22
Interface d’une classe (fichier .h)
01/04/2017 Interface d’une classe (fichier .h) class Date { public : int var1; Date(); ~Date(); void initialiser(int j,int m,int a); void afficher(); int getJour(); int getMois(); int getAnnee(); setJour( int j); setMois(int m); setAnnee(int a); protected: int _jour; int _mois; int _année; void _test(); private: }; Section public Attributs public Méthodes public Constructeur Destructeur Fonction d’accès Section protected Attributs et méthodes accessibles par les membres de la classe et des classes dérivées Section private Attributs et méthodes accessibles uniquement par les membres de la classe Vice-rectorat à la recherche / octobre 2005
23
Implantation d’une classe (fichier .cpp)
01/04/2017 Implantation d’une classe (fichier .cpp) Date::Date(){ _jour=0; _mois_0; _annee=0; } Date::~Date(){} void Data::afficher(){ cout << "la date : " << _jour ; cout << "/" << _mois ; cout << "/" << _annee << endl; int Data::getJour() {return _jours;} int Data::getMois() {return _mois;}; int Data::getAnnee(){return _annee;} Data::setJour(int j) {_jour =j;} Data::setMois(int m) {_mois =m;} Data::setAnnee(int a){_annee=a;} Définition des méthodes de la class : Constructeur Destructeur Fonction d’accès Vice-rectorat à la recherche / octobre 2005
24
Réutilisabilité On cherche à écrire des objets réutilisables.
01/04/2017 Réutilisabilité On cherche à écrire des objets réutilisables. On aimerait concevoir une classe qui modélise une pile de portée générale : une pile n’importe quels composants. pile<char> Pcar; pile <personne> Ppersonne; ==> Concevoir une classe générique Vice-rectorat à la recherche / octobre 2005
25
Facteur de qualité d’un logiciel
01/04/2017 Facteur de qualité d’un logiciel Une classe décrit une famille d ’objets : Cohérente Lisible Extensible Réutilisable Réutilisabilité Possibilité d ’obtenir toutes les occurrences d ’une classe s ’appliquant à différents types, en ne décrivant qu’une seule fois la classe. Vice-rectorat à la recherche / octobre 2005
26
Programmation générique
01/04/2017 Programmation générique L’idée de base est de passer les types de données comme paramètres pour décrire des traitements très généraux (« génériques »). Il s’agit donc d’un niveau d’abstraction supplémentaire. De tels modèles de classes/fonctions s’appellent aussi classes/fonctions génériques ou patrons (chablons), ou encore « template ». Vous en connaissez déjà sans le savoir. Par exemple la « classe » Vector de la STL n’est en fait pas une classe mais un modèle de classes : c’est le même modèle que l’on stocke des char (vector<char>), des int (vector<int>), ou tout autre objet. Vice-rectorat à la recherche / octobre 2005
27
Programmation générique
01/04/2017 Programmation générique Exemple simple pour commencer Prenons un exemple simple pour commencer : une fonction échangeant la valeur de 2 variables. Par exemple avec 2 entiers vous écririez une fonction comme : // échange la valeur de ses arguments void echange(int& i, int& j) { int tmp(i); i = j; j = tmp; } Mais vous vous rendez bien compte que vous pourriez faire la même chose (le même algorithme) avec deux double, ou même deux objets quelconques, pour peu qu’ils aient un constructeur de copie (Obj tmp(i);) et un opérateur de copie (operator=). Vice-rectorat à la recherche / octobre 2005
28
Programmation générique
01/04/2017 Programmation générique Exemple suite… L’écriture générale serait alors quelque chose comme : // échange la valeur de ses arguments void echange(Type& i, Type& j) { Type tmp(i); i = j; j = tmp; } où Type est une représentation générique du type des objets à échanger. La façon exacte de le faire en C++ est la suivante : template<typename Type> Vice-rectorat à la recherche / octobre 2005
29
Programmation générique
01/04/2017 Programmation générique Exemple suite…et fin On pourra alors utiliser la fonction echange avec tout type/classe pour lequel le constructeur de copie et l’opérateur d’affectation (=) sont définis. Par exemple : int a(2), b(4); echange(a,b); double da(2.3), db(4.5); echange(da,db); vector<double> va, vb; ... echange(va,vb); string sa("ca marche"), sb("coucou"); echange(sa, sb); Vice-rectorat à la recherche / octobre 2005
30
Programmation générique
01/04/2017 Programmation générique Second exemple d’une fonction générique template <class X > X min (const X& A,const X& B){ if (A<B) return(A); return (B); } L ’opérateur < doit être surchargé dans les classes qui utiliseront le modèle min intmain (){ int i,j,m; cin >> i,j; int m=min <int>(i,j); ... } Le compilateur instancie une fonction int min (const int&, const int&) Vice-rectorat à la recherche / octobre 2005
31
Programmation générique
01/04/2017 Programmation générique Généralisation aux classes Une classe générique est une classe avec un template. Même principe que les fonctions génériques. template <typename T> class Nb{ public : T a, b; T add (){ return a+b}; }; Nb<int> N; N.a = 1; N.b = 2; cout << N.Add() << endl; Vice-rectorat à la recherche / octobre 2005
32
Programmation générique
01/04/2017 Programmation générique Généralisation aux classes On pourrait par exemple vouloir créer une classe qui réalise une paire d’objets quelconques : template<typename T1, typename T2> class Paire { public: Paire(const T1& un, const T2& deux) : premier(un), second(deux) {} virtual ˜Paire(){} T1 get1() const { return premier; } T2 get2() const { return second; } void set1(const T1& val) { premier = val; } void set2(const T2& val) { second = val; } protected: T1 premier; T2 second; }; Vice-rectorat à la recherche / octobre 2005
33
Programmation générique
01/04/2017 Programmation générique Généralisation aux classes et par exemple créer la classe « paire string–double » : Paire<string,double> ou encore la classe « paire char–unsigned int » : Paire<char,unsigned int> Note : un tel modèle de classe existe dans la STL : pair. Les modèles de classes sont donc un moyen condensé d’écrire plein de classes potentielles à la fois. Vice-rectorat à la recherche / octobre 2005
34
Programmation générique
01/04/2017 Programmation générique Déclaration d’un modèle Pour déclarer un modèle de classe ou de fonction, il suffit de faire précéder sa déclaration du mot clé template suivit de ses arguments (qui sont donc des noms génériques de type) suivant la syntaxe : template<typename nom1, typename nom2, ...> Exemple template<typename T1, typename T2> class Paire { ... Le paramètre générique effectif d ’une classe peut être un identificateur de type prédéfini ou un identificateur de classe. Vice-rectorat à la recherche / octobre 2005
35
Programmation générique
01/04/2017 Programmation générique Déclaration d’un modèle Pour déclarer un modèle de classe ou de fonction, il suffit de faire précéder sa déclaration du mot clé template suivit de ses arguments (qui sont donc des noms génériques de type) suivant la syntaxe : template<typename nom1, typename nom2, ...> Exemple template<typename T1, typename T2> class Paire { ... Les types ainsi déclarés (paramètres du modèle) peuvent alors être utilisés dans la définition qui suit, exactement comme tout autre type. Note : on peut aussi utiliser le mot class à la place de typename, par exemple : template<class T1, class T2> Vice-rectorat à la recherche / octobre 2005
36
Programmation générique
01/04/2017 Programmation générique Définitions externes des méthodes de modèles de classes Si les méthodes d’un modèle de classes sont définies en dehors de cette classe, elle devront alors aussi être définies comme modèle et être précédées du mot clé template, mais il est de plus absolument nécessaire d’ajouter les paramètres du modèle (les types génériques) au nom de la classe [ pour bien spécifier que dans cette définition c’est la classe qui est en modèle et non la méthode. ] Exemple template<typename T1, typename T2> class Paire { public: Paire(const T1&, const T2&); ... }; // définition du constructeur template<typename T1, typename T2> Paire<T1,T2>::Paire(const T1& un, const T2& deux) : premier(un), second(deux) { } Vice-rectorat à la recherche / octobre 2005
37
Programmation générique
01/04/2017 Programmation générique Instanciation des modèles La définition des modèles ne génère en elle-même aucun code : c’est juste une description de plein de codes potentiels. Le code n’est produit que lorsque tous les paramètres du modèle ont pris chacun un type spécifique. Lors de l’utilisation d’un modèle, il faut donc fournir des valeurs pour tous les paramètres (au moins ceux qui n’ont pas de valeur par défaut). On appelle cette opération une instanciation du modèle. L’instanciation peut être implicite lorsque le contexte permet au compilateur de décider de l’instance de modèle à choisir. Par exemple, dans le code : double da(2.3), db(4.5); echange(da,db); il est clair (par le contexte) qu’il s’agit de l’instance echange<double> du modèle template<typename T> void echange(T&,T&); qu’il faut utiliser. Vice-rectorat à la recherche / octobre 2005
38
Programmation générique
01/04/2017 Programmation générique Instanciation des modèles Mais dans la plupart des cas, on explicite l’instanciation lors de la déclaration d’un objet. C’est ce qui vous faites lorsque vous déclarez par exemple vector<double> tableau; Il suffit dans ce cas de spécifier le(s) type(s) désiré(s) après le nom du modèle de classe et entre <>. L’instanciation explicite peut aussi être utile dans les cas où le contexte n’est pas suffisamment clair pour choisir. Par exemple avec le modèle de fonction template <typename Type> Type monmax(const Type& x, const Type& y) { if (x < y) return y; else return x; } l’appel monmax(3.14, 7); est ambigu. Il faudra alors écrire monmax<double>(3.14, 7); Vice-rectorat à la recherche / octobre 2005
39
Programmation générique
01/04/2017 Programmation générique Modèles, surcharge et spécialisation Les modèles de fonctions peuvent très bien être surchargés comme les fonctions usuelles (puisque, encore une fois, ce sont juste une façon condensée d’écrire plein de fonctions à la fois). Par exemple : template<typename Type> void affiche(const Type& t) { cout << "J’affiche " << t << endl; } // surcharge pour les pointeurs : on préfère ici écrire // le contenu plutôt que l’adresse. template<typename Type> void affiche(Type* t) { cout << "J’affiche " << *t << endl; Note : on aurait même pu faire mieux en écrivant : affiche<Type>(*t); qui fait appel au premier modèle. Vice-rectorat à la recherche / octobre 2005
40
Programmation générique
01/04/2017 Programmation générique Modèles, surcharge et spécialisation Mais les modèles (y compris les modèles de classes) offrent un mécanisme supplémentaire : la spécialisation qui permet de définir une version particulière d’une classe ou d’une fonction pour un choix spécifique des paramètres du modèle. Par exemple, on pourrait spécialiser le second modèle ci-dessus dans le cas des pointeurs sur des entiers : template<> void affiche<int>(int* t) { cout << "J’affiche le contenu d’un entier: " << *t << endl; } La spécialisation d’un modèle (lorsqu’elle est totale) se fait en : ajoutant template<> devant la définition nommant explicitement la classe/fonction spécifiée C’est le <int> après affiche dans l’exemple ci-dessus. Vice-rectorat à la recherche / octobre 2005
41
Programmation générique
01/04/2017 Programmation générique Exemple de spécialisation de classe template<typename T1, typename T2> class Paire { ... }; // spécialisation pour les paires <string,int> template<> class Paire<string,int> { public: Paire(const string& un, int deux) : premier(un), second(deux) {} virtual ˜Paire() {} string get1() const { return premier; } int get2() const { return second; } void set1(const string& val) { premier = val; } void set2(int val) { second = val; } // une méthode de plus void add(int i) { second += il } protected: string premier; int second; Vice-rectorat à la recherche / octobre 2005
42
Programmation générique
01/04/2017 Programmation générique Spécialisation: remarques Note 1 : La spécialisation peut également s’appliquer uniquement à une méthode d’un modèle de classe sans que l’on soit obligé de spécialiser toute la classe. Utilisée de la sorte, la spécialisation peut s’avérer particulièrement utile. Note 2 : La spécialisation n’est pas une surcharge car il n’y a pas génération de plusieurs fonctions de même nom (de plus que signifie une surcharge dans le cas d’une classe ?) mais bien une instance spécifique du modèle. Note 3 : il existe aussi des spécialisations partielles (de classe ou de fonctions), mais cela nous emmènerait trop loin dans ce cours. Vice-rectorat à la recherche / octobre 2005
43
Programmation générique
01/04/2017 Programmation générique Exemple 2 template <typename T, int taille_max = 200> // Le paramètre entier max_size a la valeur 200 par défaut. class pile { public: typedef T value_type; private: T* laPile ; int sommet ; public: pile() : sommet(0) { laPile = new T [taille_max] ; } ~pile() { delete [] laPile ; } void empiler ( T c ) { laPile[ sommet++ ] = c ; } // ... }; Vice-rectorat à la recherche / octobre 2005
44
Programmation générique
01/04/2017 Programmation générique Retour sur le typename Le mot clef typename indique que le mot suivant doit être considéré comme un type. C’est le cas dans une déclaration template (où typename peut aussi être remplacé par class) et dans les cas où il y aurait ambiguïté ou si le type n’est pas encore instancié (paramètre d’un autre type, lui-même template). template <class P, int taille_max = 200> class SacADos { // P::value_type n’est pas encore un type puisque T, n’a pas été instancié // Il faut donc l’indiquer au compilateur typedef typename P::value_type T_values; ... }; Vice-rectorat à la recherche / octobre 2005
45
Programmation générique
01/04/2017 Programmation générique Fonctions et méthodes génériques template permet aussi de déclarer des fonctions génériques, voire des fonctions membres génériques d’une classe générique. Cependant, ici, la spécification entre < et > n’est pas requise à l’appel car déduite des paramètres effectifs. int main() { pile p ; // instanciation d’une pile. char c = ’a’; // Ces appels sont valides. // Dans le premier cas ‘‘char’’ est déduit //du type de c. p.empiler(c); p.empiler<char>(c) ; explorer( p ); explorer<pile>( p ); return 0; } class pile { template <class T> void empiler(const T& a); .... }; template<class X> void explorer(const X& p) { ... }; Vice-rectorat à la recherche / octobre 2005
46
Programmation générique
01/04/2017 Programmation générique Exemple 3 Il est possible de créer des Tableaux d'entiers, de les manipuler comme les variables d'un type intégré et de les passer en paramètre sans avoir à fournir la taille comme paramètre supplémentaire. Cependant notre solution ne fonctionne que pour des Tableaux d'entiers. Que se passe-t-il si on avait besoin de Tableaux de doubles ou de chaînes de caractères? On voudrait pouvoir créer des Tableaux de types arbitraires sans avoir à définir une classe distincte pour chacun d'eux. Cela peut être fait à l'aide des modèles (templates). Vice-rectorat à la recherche / octobre 2005
47
Programmation générique
01/04/2017 Programmation générique template<class elem> class Tableau{ private: int nbelements; int *T; public: Tableau(int); Tableau(const Tableau&); ~Tableau() {delete[] T;}; int longueur() const {return nbelements;}; elem& operator[](int) const; Tableau& operator=(const Tableau&); }; Rappel: on peut écrire template <class elem> outemplate <typename elem> Vice-rectorat à la recherche / octobre 2005
48
Programmation générique
01/04/2017 Programmation générique template<class elem> Tableau<elem>::Tableau(int n) { T=new elem[n]; nbelements=n; } template<class elem> Tableau<elem>::Tableau(const Tableau<elem> &tab) T=new elem[tab.nbelements]; (*this)=tab; template<class elem> elem& Tableau<elem>::operator[](const int i) const return T[i]; Vice-rectorat à la recherche / octobre 2005
49
Programmation générique
01/04/2017 Programmation générique template<class elem> Tableau<elem>& Tableau<elem>::operator=(const Tableau<elem> &tab) { if (nbelements<tab.nbelements) delete[] T; T=new elem[tab.nbelements]; } nbelements=tab.nbelements; for (int i=0; i<nbelements; i++) T[i]=tab.T[i]; return *this; Vice-rectorat à la recherche / octobre 2005
50
Programmation générique
01/04/2017 Programmation générique La définition de la classe est précédée de templace<class elem> qui indique que l'on défini un modèle et qu'un type elem apparaît en argument dans la déclaration de Tableau. On peut alors déclarer un tableau d'entiers ou de double de la façon suivante: Tableau<int> iTab(100); Tableau<double> dTab(20); Vice-rectorat à la recherche / octobre 2005
51
Programmation générique
01/04/2017 Programmation générique Les modèles ne sont pas retreints aux classes comme on l’a déjà vu et comme l'illustre les exemples suivants: template<class C> void echanger(C &A, C &B) { C tmp=A; A=B; B=tmp; } template<class elem> void print(const Tableau<elem>& t) for (int i=0; i<t.longueur(); i++) cout<<t[i]<<" "; cout<<endl; Vice-rectorat à la recherche / octobre 2005
52
Programmation générique
01/04/2017 Programmation générique La fonction echanger ne peut être utilisée que par une classe C possédant un constructeur de copie et un opérateur d'affectation. La fonction print nécessite que l'opérateur << soit défini pour les objets de type elem. On peut appeler ces fonctions de la façon suivante: int a=0, b=1; double x=1.0, y=2.3; Tableau<int> A(20), B(30); Tableau<char> C(100) // // initialisation de A, B et C echanger(a,b); echanger(x,y); echanger(A,B); print(A); print(B); print(C); Vice-rectorat à la recherche / octobre 2005
53
Programmation générique
01/04/2017 Programmation générique Modèles de classe et compilation séparée Les modèle de classes doivent nécessairement être définis au moment de leur instanciation afin que le compilateur puisse générer le code correspondant. Ce qui implique, lors de compilation séparée, que les fichiers d’en-tête (.h) doivent contenir non seulement la déclaration, mais également la définition complète de ces modèles !! On ne peut donc pas séparer la déclaration de la définition dans différents fichiers... Ce qui présente plusieurs inconvénients : Les mêmes instances de modèles peuvent être compilées plusieurs fois, et se retrouvent en de multiples exemplaires dans les fichiers exécutables. On ne peut plus cacher leurs définitions (par exemple pour des raisons de confidentialité, protection contre la concurrence, etc...) Vice-rectorat à la recherche / octobre 2005
54
Programmation générique
01/04/2017 Programmation générique Modèles de classe et compilation séparée Notez également que les fichiers contenant des modèles ne sont pas des fichiers sources classiques, puisque sans instanciation, ils ne génèrent aucun code machine. Pour résoudre ces problèmes, le langage C++ donne en principe la possibilité d’exporter les définitions des modèles dans des fichiers complémentaires à l’aide de la directive export. Malheureusement celle-ci n’est pour l’instant (à ma connaissance) pas gérée par les compilateurs actuels : warning: keyword ‘export’ not implemented, and will be ignored En l’état, il faut donc malheureusement joindre prototype et définitions des modèles dans les mêmes fichiers d’en-tête. Le seul « point noir » des templates est donc lié à la faiblesse des compilateurs actuels qui ne gèrent pas la directive export. Vice-rectorat à la recherche / octobre 2005
55
Programmation générique
01/04/2017 Programmation générique Templates Déclarer un modèle de classe ou de fonction : template<typename nom1, typename nom2, ...> Définition externe des méthodes de modèles de classes : NomClasse<nom1, nom2, ...>::NomMethode(... Instanciation : spécifier simplement les types voulus après le nom de la classe/fonction, entre <> (Exemple : vector<double>) Spécialisation (totale) de modèle pour les types type1, type2... : template<> NomModele<type1,type2,...> ...suite de la declaration... Compilation séparée : pour les templates, il faut tout mettre (déclarations et définitions) dans le fichier d’en-tête (.h). Vice-rectorat à la recherche / octobre 2005
56
Le traitement des exceptions
01/04/2017 Le traitement des exceptions Le C++ contient un mécanisme très utile pour traiter les erreurs et autres exceptions. Lorsqu'une erreur est détectée, il est possible de transmettre (throw) un objet à une partie du code qui reçoit (catch) l'objet et traite l'erreur. Considérons, par exemple, la fonction operator[] de la classe Tableau. Il Serait prudent de vérifier la valeur de l'opérande afin de s'assurer qu'elle corresponde bien à un indice valide du tableau. Si tel n'est pas le cas, que doit faire la fonction? Elle ne dispose pas de l'information nécessaire pour traiter elle-même l'erreur et ne peut pas signaler l'erreur par une valeur de retour ou un paramètre. Pour régler ce problème nous allons ajouter un membre publique et modifier la fonction operator[]. Vice-rectorat à la recherche / octobre 2005
57
Le traitement des exceptions
01/04/2017 Le traitement des exceptions template<class elem> class Tableau{ elem nbelements; elem *T; public: struct erreur_indice { int indice; erreur_indice(int i){indice=i;}; }; Tableau(int); Tableau(const Tableau&); ~Tableau() {delete[] T;}; int longueur() const {return nbelements;}; elem& operator[](int) const; Tableau& operator=(const Tableau&); Vice-rectorat à la recherche / octobre 2005
58
Le traitement des exceptions
01/04/2017 Le traitement des exceptions template<class elem> elem& Tableau<elem>::operator[](const int i) const { if (i<0 || i>=nbelements) throw erreur_indice(i); return T[i]; } Nous avons ajouté à Tableau la définition d'une structure afin de définir un type d'erreur correspondant aux dépassement des limites. En C++ une structure est exactement comme une classe sauf que par défaut les membres sont publiques. Si operator[] détecte une erreur, il utilisera le constructeur de erreur_indice pour créer un objet qu'il transmettra via l'instruction throw. L'instruction throw se comporte un peu comme l'instruction return sauf qu'elle cherche dans la pile des appels de fonctions celle qui demande à recevoir l'objet. Vice-rectorat à la recherche / octobre 2005
59
Le traitement des exceptions
01/04/2017 Le traitement des exceptions template<class elem> void initialise(Tableau<elem>& t, const elem& E) { for (int i=0; i<=t.longueur(); i++) t[i]=E; } int main() Tableau<int> T(100); try initialise(T,0); catch(Tableau<int>::erreur_indice e) cerr<<"Erreur d'indice: <<e.indice<<endl; return 0; Exemple: Vice-rectorat à la recherche / octobre 2005
60
Le traitement des exceptions
01/04/2017 Le traitement des exceptions La fonction main() utilise les mots clefs try et catch pour indiquer qu'elle appelle la fonction initialise et qu'elle désire traiter les erreurs de type Tableau<int>::erreur_indice. Au dernier tour de boucle de initialise, l'indice i aura dépassé les limites du tableau et Tableau<int>::operator[] transmettra un objet de type Tableau<int>::erreur_indice qui sera reçu par la fonction main(). Vice-rectorat à la recherche / octobre 2005
61
Le traitement des exceptions
01/04/2017 Le traitement des exceptions Plusieurs catch peuvent apparaître après un try afin de recevoir et traiter plusieurs types d'erreurs. L'expression catch(...) permet de recevoir tous les types d'erreurs non capturés par les catch précédents: try{ instructions } catch(type 1){ catch(type 2){ catch(...){ Vice-rectorat à la recherche / octobre 2005
Présentations similaires
© 2024 SlidePlayer.fr Inc.
All rights reserved.