Télécharger la présentation
La présentation est en train de télécharger. S'il vous plaît, attendez
Publié parSévérine Lelievre Modifié depuis plus de 9 années
1
Chapitre VII Techniques plus avancées à travers le concept de classe
2
Chapitre VII - Techniques plus avancées à travers le concept de classe2 Prototypes des classes Définition semblable aux prototypes de fonctions. Permet d’indiquer au compilateur qu’un nom utilisé dans le programme doit être considéré comme une classe. Ex.: class Point_2D; class Polygone_2D; class Objet_2D { protected: Polygone_2D * Q; Point_2D * Ref; Références avancées
3
Chapitre VII - Techniques plus avancées à travers le concept de classe3 Prototypes des classes Nécessaire si vous ne disposez pas de la définition complète de la classe. Insuffisant si vous souhaitez appeler les fonctions membres de cette classe. Ex.: public: void Init_Polygone_2D(Polygone_2D * Q); void Init_Reference_2D(Point_2D * Ref); Polygone_2D * Donne_Polygone_2D(); Point_2D * Donne_Point_2D(); }; (suite) Utile pour masquer les détails d’implantation d’une classe. Références avancées
4
Chapitre VII - Techniques plus avancées à travers le concept de classe4 Classes « proxy » (par procuration) Il est préférable de masquer les détails d’implantation d’une classe afin d’empêcher: -l’accès aux informations privées, -l’accès aux programmes brevetés dans une classe. En fournissant à vos clients une classe proxy, ne connaissant que l’interface public de votre classe, cela leur permet d’utiliser vos services sans leur donner accès aux détails d’implantation. La mise en place d’une classe proxy nécessite plusieurs étapes comme nous verrons dans l’exemple simple suivant :
5
Chapitre VII - Techniques plus avancées à travers le concept de classe5 Exemple d’une classe « proxy » Étape I :Création de la définition de la classe et de ses fichiers d’implantation dont les détails d’implantation doivent être masqués. class Implantation { private : int valeur; public : Implantation( int v ){ valeur = v; } void ajusterValeur( int v ){ valeur = v; } int lectureValeur() const{ return valeur; } };
6
Chapitre VII - Techniques plus avancées à travers le concept de classe6 Exemple d’une classe « proxy » Étape II : Création de la définition d’une classe proxy avec une interface public identique à celle de la classe Implantation. Note :Lorsqu’une définition de classe n’utilise qu’un pointeur vers une autre classe, le fichier d’en-tête pour cette autre classe n’a pas besoin d’être inclus par le mot-clé #include. class Implantation; class Interface { private : Implantation * ptr; public : Interface( int v ); void ajusterValeur( int v ); int lectureValeur() const; }; Même interface public Le seul membre privé. La seule référence à la classe Implantation. Définition du prototype de classe
7
7 Exemple d’une classe « proxy » Étape III : Création du fichier d’implantation de la classe proxy. Note :Le fichier Interface.cpp est fourni au client comme fichier d’objet précompilé avec celui d’en-tête interface.h. Puisque le fichier interface.cpp n’est mis à la disposition du client que sous la forme de code d’objet compilé, le client ne peut voir les interactions entre les classes proxy et propriétaire. #include "Interface.h" #include "Implantation.h" Interface::Interface( int v) : ptr (new Implantation( v ) ) { } void Interface::ajusterValeur( int v ) {ptr -> ajusterValeur( v ); } int Interface::lectureValeur() const {return ptr -> lectureValeur(); } Appel des fonctionnalités de la classe Implantation. Le seul fichier à inclure le fichier d’en-tête Implantation.h.
8
8 Exemple d’une classe « proxy » Étape IV : Programme qui teste la classe Interface Note :On n’y mentionne aucunement l’existence de la classe séparée Implantation. #include #include "Interface.h" void main() { Interface i( 5 ); cout << i.lectureValeur() << endl; i.ajusterValeur( 10 ); cout << i.lectureValeur() << endl; } On inclut ce seul fichier d’en-tête.
9
9 Portées de nom La partie d’un programme dans laquelle un nom est visible et possède une signification donnée. Portée locale : Les noms des variables paramètres définis dans l’entête d’une fonction. Portée de bloc : Une variable déclarée à l’intérieur d’une boucle : for (int i = 1; i <10; i++) { cout << i << endl; } // i n’est plus défini. Une variable déclarée à l’intérieur d’un bloc : { int t = x; x = y; y = t; } // t n’est plus défini. Note :La portée locale est un cas particulier de portée de bloc.
10
10 Portées de nom Portée de classe : Un champ de données ou une fonction membre déclarée private ne peut être employée qu’au sein des fonctions membres de la classe. Portée globale : Les variables déclarées à l’extérieur de fonctions et de définitions de classes. La portée globale va du point de définition à la fin du fichier où apparaît la variable. Portée de fichier : La portée des variables déclarées static ne change pas. Elles conservent leurs valeurs lorsque nous quittons la portée de celles-ci car leur durée de vie est globale. Les variables globales déclarées static possèdent une portée de fichier i.e. qu’elles ne possèdent de signification qu’au sein du fichier dans lequel elles sont déclarées. Celles non déclarées comme static peuvent être partagées entre plusieurs fichiers (voir le cas suivant).
11
11 Portées de nom Portée de plusieurs fichiers : Une variable globale déclarée extern dans chaque fichier où elle est employée sans être définie. Cela est remplacé maintenant par la notion d’espaces de noms. ATTENTION : Un nom peut masquer (ombrager) un nom d’une portée environnante. // N'écrivez pas de code comme le suivant ! double solde; class Compte {private :double solde;public :Compte(double solde); }; Compte::Compte(double solde) {this -> solde = solde;// donnée membre et paramètre ::solde = solde;// variable globale et paramètre }
12
12 Ombrage et héritage #include class Employe { protected :float salaire; public:virtual void Init_salaire(float nouveau_salaire); }; class Gestionnaire : public Employe {public:virtual void Init_salaire(float nouveau_salaire); }; void Employe::Init_salaire(float nouveau_salaire) {salaire = nouveau_salaire;cout << salaire << endl; } void Gestionnaire::Init_salaire(float nouveau_salaire) {salaire = 1.05f * nouveau_salaire;cout << salaire << endl; } void main() {Employe * p = new Gestionnaire(); p -> Init_salaire(100.0f); } 1 er cas :une classe dérivée déclare une fonction membre virtuelle possédant les mêmes arguments qu’une fonction membre de la classe de base. 105
13
13 Ombrage et héritage #include class Employe { protected :float salaire; public:void Init_salaire(float nouveau_salaire); }; class Gestionnaire : public Employe {public:void Init_salaire(float nouveau_salaire); }; void Employe::Init_salaire(float nouveau_salaire) {salaire = nouveau_salaire;cout << salaire << endl; } void Gestionnaire::Init_salaire(float nouveau_salaire) {salaire = 1.05f * nouveau_salaire;cout << salaire << endl; } void main() {Employe * p = new Gestionnaire(); p -> Init_salaire(100.0f); } 2 ième cas : une classe dérivée déclare une fonction membre non virtuelle possédant les mêmes arguments qu’une fonction membre de la classe de base. 100
14
14 Ombrage et héritage #include class Employe { protected :float salaire; public:void Init_salaire(float nouveau_salaire); }; class Gestionnaire : public Employe {public:void Init_salaire(float nouveau_salaire, float bonus); }; void Employe::Init_salaire(float nouveau_salaire) {salaire = nouveau_salaire;cout << salaire << endl; } void Gestionnaire::Init_salaire(float nouveau_salaire, float bonus) {salaire = nouveau_salaire + bonus;cout << salaire << endl; } void main() {Employe * p = new Gestionnaire(); p -> Init_salaire(100.0f, 20.0f); // invalide à moins que p soit remplacé } // par ((Gestionnaire *) p) 3 ième cas : une classe dérivée déclare une fonction membre non virtuelle ne possédant pas les mêmes arguments qu’une fonction membre de la classe de base.
15
15 Ombrage et héritage #include class Employe { protected :float salaire; public:void Init_salaire(float nouveau_salaire); }; class Gestionnaire : public Employe {public:void Init_salaire(float nouveau_salaire, float bonus); }; void Employe::Init_salaire(float nouveau_salaire) {salaire = nouveau_salaire;cout << salaire << endl; } void Gestionnaire::Init_salaire(float nouveau_salaire, float bonus) {salaire = nouveau_salaire + bonus;cout << salaire << endl; } void main() {Gestionnaire * p = new Gestionnaire(); p -> Init_salaire(100.0f, 20.0f);// la fonction à 1 paramètre est masquée }// (invalide) à moins que p soit remplacé // par ((Employe *) p).
16
Chapitre VII - Techniques plus avancées à travers le concept de classe16 Ombrage et héritage #include class Employe { protected :float salaire; public:virtual void Init_salaire(float nouveau_salaire); }; class Gestionnaire : public Employe {public:virtual void Init_salaire(float nouveau_salaire, float bonus); }; void Employe::Init_salaire(float nouveau_salaire) {salaire = nouveau_salaire;cout << salaire << endl; } void Gestionnaire::Init_salaire(float nouveau_salaire, float bonus) {salaire = nouveau_salaire + bonus;cout << salaire << endl; } void main() {Employe * p = new Gestionnaire(); p -> Init_salaire(100.0f, 20.0f);// invalide à moins que p soit remplacé }// par ((Gestionnaire *) p). 4 ième cas : une classe dérivée déclare une fonction membre virtuelle ne possédantpas les mêmes arguments qu’une fonction membre de la classe de base.
17
Chapitre VII - Techniques plus avancées à travers le concept de classe17 Ombrage et héritage #include class Employe { protected :float salaire; public:virtual void Init_salaire(float nouveau_salaire); }; class Gestionnaire : public Employe {public:virtual void Init_salaire(float nouveau_salaire, float bonus); }; void Employe::Init_salaire(float nouveau_salaire) {salaire = nouveau_salaire;cout << salaire << endl; } void Gestionnaire::Init_salaire(float nouveau_salaire, float bonus) {salaire = nouveau_salaire + bonus;cout << salaire << endl; } void main() {Gestionnaire * p = new Gestionnaire(); p -> Init_salaire(100.0f);// invalide à moins que p soit remplacé } // par ((Employe *) p).
18
Chapitre VII - Techniques plus avancées à travers le concept de classe18 Ombrage et héritage #include class Employe { protected :float salaire; public:virtual void Init_salaire(float nouveau_salaire); }; class Gestionnaire : public Employe {public:virtual void Init_salaire(float nouveau_salaire); virtual void Init_salaire(float nouveau_salaire, float bonus); }; void Employe::Init_salaire(float nouveau_salaire) {salaire = nouveau_salaire;cout << salaire << endl; } void Gestionnaire::Init_salaire(float nouveau_salaire, float bonus) {salaire = nouveau_salaire + bonus;cout << salaire << endl; } void Gestionnaire::Init_salaire(float nouveau_salaire) { Employe::Init_salaire(nouveau_salaire); } On peut aussi déclarer les 2 fonctions dans la même portée.
19
Chapitre VII - Techniques plus avancées à travers le concept de classe19 Ombrage et héritage void main() {Gestionnaire * p = new Gestionnaire(); p -> Init_salaire(100.0f); p -> Init_salaire(100.0f, 20.0f); }
20
Chapitre VII - Techniques plus avancées à travers le concept de classe20 Fonctions et classes amies ou déclaration d’amitié Doit représenter une exception. Le mot clé friend est utilisé pour permettre à des fonctions ou à des classes d’accéder aux membres privés et protégés d’une autre classe. Lève l’interdiction d’accès liée aux étiquettes private et protected. Les fonctions et classes amies sont spécifiées dans la classe pour laquelle vous souhaitez accéder aux membres; par contre, elles sont définies en dehors de sa portée. Mot clé friend Une fonction déclarée en tant qu’amie peut accéder à tous les membres d’une classe exactement de la même manière que si elle appartenait à cette classe. Cela ne dispense pas toutefois les fonctions amies de créer un objet de la classe. Cette fonction amie est une fonction membre de la classe. Cela peut améliorer la performance.
21
Chapitre VII - Techniques plus avancées à travers le concept de classe21 Fonctions et classes amies S’applique à la fois aux fonctions classiques et aux fonctions membres des classes. Pour une classe amie, toutes les fonctions membres de cette classe seront considérées comme des fonctions amies. Ex.: class Systeme_de_paye { ….. friend void Comptabilite::Escompte(); ….. }; La fonction Escompte de la classe Comptabilite est considérée comme une fonction amie de la classe Systeme_de_paye.
22
22 Fonctions et classes amies Les notions d’accès aux membres private, protected et public ne s’appliquent pas aux déclarations d’amitié; ces dernières peuvent donc être placées n’importe où dans la définition de la classe. La déclaration d’amitié est accordée et non réclamée, non prise, ni acceptée. Ex. : Pour qu’une classe B devienne amie d’une classe A, cette dernière doit explicitement déclarer que la classe B est son amie. La déclaration d’amitié n’est ni symétrique, ni transitive. Ex. : Si on déclare la classe A amie de la classe B et on déclare la classe B amie de la classe C, B est amie de A (l’amitié n’est pas symétrique) A est amie de C (l’amitié n’est pas transitive). L’amitié n’est pas héritée. Si une classe de base est amie d’une autre classe, cela ne signifie pas que les classes dérivées soient des amies. Il est possible de spécifier des fonctions surchargées comme amies d’une classe. Chacune doit être déclarée explicitement comme amie de cette classe.
23
Chapitre VII - Techniques plus avancées à travers le concept de classe23 Fonctions et classes amies Il existe plusieurs situations d’amitié. Fonction indépendante, amie d’une classe A class A { …….. friend ----- fct(------); …….. }; La fonction fct ayant le prototype spécifié est autorisée à accéder aux membres privés ou protégés de la classe A.
24
Chapitre VII - Techniques plus avancées à travers le concept de classe24 Fonctions et classes amies Il existe plusieurs situations d’amitié. Fonction membre d’une classe B, amie d’une autre classe A class A { …….. friend ----- B::fct(------); …….. }; La fonction fct, membre de la classe B, ayant le prototype spécifié est autorisée à accéder aux membres privés ou protégés de la classe A. Pour compiler cette déclaration, le compilateur devra savoir que B est une classe.
25
Chapitre VII - Techniques plus avancées à travers le concept de classe25 Fonctions et classes amies Il existe plusieurs situations d’amitié. Toutes les fonctions membres d’une classe B sont amies d’une autre classe A class A { …….. friend class B; …….. }; Pour compiler cette déclaration, le compilateur devra savoir que B est une classe.
26
Chapitre VII - Techniques plus avancées à travers le concept de classe26 Fonctions et classes amies Exemple : #include class Compte { friend void ajusterX(Compte & C, int val); private :int x; public :Compte() { x = 0; } void affichage() const {cout << x << endl; } }; void ajusterX(Compte & C, int val) { C.x = val; } légal
27
Chapitre VII - Techniques plus avancées à travers le concept de classe27 Fonctions et classes amies Exemple : void main() { Compte compteur; compteur.affichage(); ajusterX(compteur, 8); compteur.affichage(); } 0808 La fonction prend compteur comme argument plutôt que d’utiliser un identificateur: Compteur.ajusterX( 8 );
28
28 Membres statiques Rappel:Le mot clé static permet à une variable locale de conserver sa valeur tout au long de l’exécution du programme. Peut aussi être utilisé dans un autre contexte, pour les données et fonctions membres des classes. Données membres statiques La création d’un objetL’allocation des données membres statiques de cette classe. Elles sont indépendantes des objets i.e. une donnée membre statique est unique pour l’ensemble des objets de la classe. Tous les objets de la classe accèdent à la même donnée membre. Utilisez des membres de données static pour économiser la mémoire lorsqu’une seule copie des données peut suffire.
29
29 Données membres statiques Définition Faire précéder la déclaration de la donnée membre par static. Créer cette variable en dehors de la classe. class telephone { protected: int numero_local; char nom[20+1];char prenom[20+1]; public: static int Nombre_appels;// Déclaration insuffisante. ….. }; int telephone::Nombre_appels = 0;// Initialisée une seule fois. // Le mot-clé static doit être absent. /* On doit la déclarer en dehors de la classe car elle ne sera pas allouée au moment de la création des objets de la classe. */
30
30 Données membres statiques Il s’agit d’une donnée membre appartenant à la classe; elle doit respecter dans ces conditions l’étiquette de protection qui lui a été attribuée. Mode d’accès à cette variable (1)telephone :: Nombre_appels += 1;// avec le nom de la classe // lorsque l’étiquette est public. (2)telephone T;// avec un nom d’objet T.Nombre_appels += 1;// lorsque l’étiquette est public. (3)telephone * pT; pT = new telephone; pT -> Nombre_appels += 1;// avec un pointeur vers l’objet delete pT; // lorsque l’étiquette est public. Lorsque l’étiquette n’est pas public, l’accès aux membres de données static d’une classe doit passer obligatoirement par le biais de fonctions membres public ou amies de cette classe.
31
31 Données membres statiques Les membres static d’une classe existent même s’il n’y a encore aucun objet de cette classe. Pour accéder à un membre de donnée static identifié public lorsqu’aucun objet de la classe n’existe, placez en préfixe du nom du membre de données le nom de classe et l’opérateur ::. Pour accéder à un membre de donnée static déclaré comme private ou protected, lorsqu’aucun objet de la classe n’existe, une fonction membre static public doit être fournie. Elle doit être appelée en préfixant son nom du nom de la classe et de l’opérateur ::. Certaines organisations adoptent la norme suivante : tous les appels vers des fonctions membres static sont effectués en conjonction avec l’identificateur du nom de la classe et non avec les identificateurs des objets.
32
Chapitre VII - Techniques plus avancées à travers le concept de classe32 Données membres statiques Exemple: classe "Aire_de_stationnement" Supposons qu’il existe plusieurs aires de stationnement et que l’on désire connaître le nombre total de véhicules présents dans ces aires de stationnement. Fichier " Stationnement.h " class Aire_de_stationnement { protected: int Nb_places;// Capacité maximale int Nb_places_disponibles;// #places disp. dans 1’aire static int Nb_total_clients; /* # véhicules stationnés dans les aires de stat.*/
33
Chapitre VII - Techniques plus avancées à travers le concept de classe33 Données membres statiques Fichier " Stationnement.h "(suite) public: Aire_de_stationnement(int Capacite); /*Permet de créer une aire de stationnement dont la capacité est fixée en paramètre. Pré -Nil. Post -Une aire de stationnement vide est créée.*/ void Arrivee_voiture(); /*Simule l'arrivée d'une nouvelle voiture. Pré -L'aire de stationnement est créée et des places sont disponibles. Post -Une place de moins est disponible.*/
34
Chapitre VII - Techniques plus avancées à travers le concept de classe34 Données membres statiques Fichier " Stationnement.h "(suite) void Depart_voiture(); /*Simule le départ d'une voiture du stationnement. Pré -L'aire de stationnement est créée et n'est pas vide. Post -Une place de plus est disponible.*/ bool Capacite_maximum_atteinte(); /*Vérifie s'il existe des places disponibles. Pré -L'aire de stationnement est créée. Post -Retourne true si aucune place n'est disponible. False autrement.*/
35
Chapitre VII - Techniques plus avancées à travers le concept de classe35 Données membres statiques Fichier " Stationnement.h "(suite) int Nb_total_de_vehicules(); /*Fournit le nombre total de véhicules stationnés dans les aires de stationnement. Pré -Nil Post -Retourne le nombre total de véhicules stationnés.*/ }; class Stationnement_pour_handicapes : public Aire_de_stationnement { public: Stationnement_pour_handicapes(); };
36
Chapitre VII - Techniques plus avancées à travers le concept de classe36 Données membres statiques Fichier " Stationnement.cpp " #include "Stationnement.h" int Aire_de_stationnement::Nb_total_clients = 0; Aire_de_stationnement::Aire_de_stationnement(int Capacite) { Nb_places = Capacite; Nb_places_disponibles = Capacite; } void Aire_de_stationnement::Arrivee_voiture() { Nb_places_disponibles = Nb_places_disponibles - 1; Nb_total_clients = Nb_total_clients + 1; }
37
Chapitre VII - Techniques plus avancées à travers le concept de classe37 Données membres statiques Fichier " Stationnement.cpp " (suite) void Aire_de_stationnement::Depart_voiture() { Nb_places_disponibles = Nb_places_disponibles + 1; Nb_total_clients = Nb_total_clients - 1; } bool Aire_de_stationnement::Capacite_maximum_atteinte() { if (Nb_places_disponibles == 0) return true; else return false; }
38
Chapitre VII - Techniques plus avancées à travers le concept de classe38 Données membres statiques Fichier " Stationnement.cpp " (suite) int Aire_de_stationnement::Nb_total_de_vehicules() { return Nb_total_clients; } Stationnement_pour_handicapes::Stationnement_pour_handicapes() : Aire_de_stationnement(3) { }
39
Chapitre VII - Techniques plus avancées à travers le concept de classe39 Données membres statiques Fichier " Application Stationnement.cpp " #include #include "Stationnement.h" void main() { Aire_de_stationnement A1(25); Aire_de_stationnement A2(50); Stationnement_pour_handicapes S;S.Arrivee_voiture();A1.Arrivee_voiture(); A1.Arrivee_voiture();A2.Arrivee_voiture(); cout<< "Nombre total de vehicules stationnees: " << S.Nb_total_de_vehicules(); }
40
Chapitre VII - Techniques plus avancées à travers le concept de classe40 Données membres statiques Une donnée membre statique peut être utilisée pour échanger des informations entre les différents objets d’une classe. Une donnée membre définie comme une constante (const) doit être déclarée en statique. Exemple class sphere { ….. public: const static float PI; }; const float sphere::PI=3.14159; Tous les objets de type sphere partagent cette même constante.
41
Chapitre VII - Techniques plus avancées à travers le concept de classe41 Fonctions membres statiques Ne sont pas utilisées pour un objet spécifique. Souvent employées pour manipuler les données statiques de la classe. Permet de mettre en œuvre un traitement qui n’est pas lié aux objets de la classe. Une fonction membre statique ne sait pas travailler sur un objet particulier de la classe (ne récupère pas le pointeur this). Ne peut accéder aux données membres associées à un objet ou appeler une fonction membre non statique. Définition: placer le mot clé static au début de son prototype. Les seuls membres que vous pouvez utiliser sans créer d’objet sont les données et fonctions membres statiques.
42
Chapitre VII - Techniques plus avancées à travers le concept de classe42 Fonctions membres statiques Exemple: classe "Aire_de_stationnement " (revu) static int Nb_total_de_vehicules();Fichier " Stationnement.h " : Fichier " Application Stationnement.cpp " void main() { Aire_de_stationnement A1(25); Aire_de_stationnement A2(50); Stationnement_pour_handicapes S;S.Arrivee_voiture();A1.Arrivee_voiture(); A1.Arrivee_voiture();A2.Arrivee_voiture(); cout<< "Nombre total de vehicules stationnees: " << Aire_de_stationnement::Nb_total_de_vehicules(); }
43
Chapitre VII - Techniques plus avancées à travers le concept de classe43 Objets constants (const) Principe du moindre privilège : fondamental d’une bonne conception de logiciels. Le mot-clé const peut être utilisé pour spécifier qu’un objet ne peut être modifié. const Temps Midi(12, 0, 0); Les tentatives de modification sur l’objet seront interceptées au moment de la compilation : C++ empêche tout appel de fonctions membres via des objets const, à moins que ces dernières elles-mêmes ne soient également déclarées const. Cela est vrai même pour des fonctions membres qui ne modifient aucunement l’objet. Déclarez const toutes les fonctions membres qui n’ont pas besoin de modifier l’objet courant pour les utiliser au besoin sur un objet const.
44
Chapitre VII - Techniques plus avancées à travers le concept de classe44 Définition de fonctions membres de type const Une fonction membre constante ne peut pas modifier les données membres de la classe. Chaque fonction membre d’une classe qui n’a pas besoin de modifier les données membres de la classe devrait être déclarée comme une fonction constante. Protège les données membres. Le mot clé const doit être indiqué à la fois à la fin du prototype de la méthode et à la fin de la première ligne du corps de cette méthode. Le compilateur fera la vérification que les attributs de l’objet ne seront pas modifiés dans cette méthode. La déclaration d’un constructeur ou d’un destructeur comme const constitue une erreur de syntaxe. Il en est de même de la déclaration comme const d’une fonction membre static.
45
Chapitre VII - Techniques plus avancées à travers le concept de classe45 Définition de fonctions membres de type const Exemple class client { protected: char Nom[20+1]; char Prenom[20+1]; public: void Afficher() const; }; void client::Afficher() const { cout << Nom << " " << Prenom; // strcpy(Nom, " " ); est impossible. }
46
Chapitre VII - Techniques plus avancées à travers le concept de classe46 Définition de fonctions membres de type const Exemple class Fraction { private: int numerateur, denominateur; public: Fraction(int num, int den); Fraction(const Fraction & F); int Numerateur() const; int Denominateur() const; }; Fraction::Fraction(int num, int den) { numerateur = num; denominateur = den; } int Fraction::Numerateur() const { return numerateur; } int Fraction::Denominateur() const { return denominateur; } Fraction::Fraction(const Fraction & F) { numerateur = F.Numerateur(); denominateur = F.Denominateur(); }
47
Chapitre VII - Techniques plus avancées à travers le concept de classe47 Erreur classique Exemple class Produit {... void Impression() const;... }; Vous devez déclarer toutes les fonctions d’accès en C++ avec le mot clé const. Si vous ne respectez pas cette règle, vous construisez des classes inutilisables par d’autres programmeurs. Supposons que la fonction Impression() n’ait pas été déclaré comme const et qu’un autre programmeur se serve de la classe Produit pour créer une classe Commande comme dans la diapositive suivante.
48
Chapitre VII - Techniques plus avancées à travers le concept de classe48 Erreur classique class Commande { public :... void Impression() const; private : string client; Produit article;... }; void Commande::Impression() const { cout << client << "\n"; article.Impression();// Erreur si Produit::Impression n’est pas const. }
49
Chapitre VII - Techniques plus avancées à travers le concept de classe49 Définition d’objets et de fonctions membres const Exemple (Deitel et Deitel, Comment programmer en C++) class Temps { private: int heure;int minute;int seconde; public: Temps(int h = 0, int m = 0, int s = 0);// Constructeur // Fonctions d’écriture void ajusterTemps(int h, int m, int s); void ajusterHeure(int h); void ajusterMinute(int m); void ajusterSeconde(int s);
50
50 Définition d’objets et de fonctions membres const // Fonctions de lecture (normalement déclarées const) int lectureHeure() const; int lectureMinute() const; int lectureSeconde() const; // Fonctions d’affichage (normalement déclarées const) void afficherMilitaire() const; void afficherStandard(); // en guise d’illustration }; -------------------------------------------------------------- ……. int Temps::lectureHeure() const { return heure; } int Temps::lectureMinute() const { return minute; } int Temps::lectureSeconde() const { return seconde; }
51
Chapitre VII - Techniques plus avancées à travers le concept de classe51 Définition d’objets et de fonctions membres const void Temps::afficherMilitaire() const // format militaire: HH:MM:SS { cout<< (heure < 10 ? "0" : "" ) << heure << ":" << (minute < 10 ? "0" : "" ) << minute << (seconde < 10 ? "0" : "" ) << seconde; } void Temps::afficherStandard()// format standard: HH:MM:SS AM (ou PM) { cout<< ((heure == 12) ? 12: heure % 12 ) << ":" << (minute < 10 ? "0" : "" ) << minute << ":" << (seconde < 10 ? "0" : "" ) << seconde << (heure < 12 ? " AM" : " PM" ); } --------------------------------------------------------------
52
Chapitre VII - Techniques plus avancées à travers le concept de classe52 Définition d’objets et de fonctions membres const ….. void main() { Temps lever( 6, 45, 0);// Objet non constant const Temps midi(12, 0, 0); // Objet constant // Fonction membreObjet lever.ajusterHeure( 18 );// non constantnon constant midi.ajusterHeure( 12 );// non constantconstant lever.lectureHeure();// constantnon constant midi.lectureMinute();// constantconstant midi.afficherMilitaire();// constantconstant midi.afficherStandard();// non constantconstant } -------------------------------------------------------------- incorrect
53
53 Définition d’objets et de fonctions membres const : constructeurs Même si un constructeur doit être une fonction membre non const, on peut l’appeler comme un objet const. À partir de l’appel du constructeur, l’invocation d’une fonction membre non const est permise pour un objet const. Temps::Temps(int h, int m, int s)// Constructeur {ajusterTemps(h, m, s); } Lorsqu’on déclare const un membre de données d’une classe, un initialiseur de membre doit fournir au constructeur sa valeur initiale pour un objet de la classe. class Increment { private : int compteur;const int increment; … public : Increment(int c = 0, int i = 1); ….}; Increment::Increment(int c, int i) : increment(i) { compteur = c;} ….
54
Chapitre VII - Techniques plus avancées à travers le concept de classe54 Conversion de pointeurs d’objets Rappel: Il est possible d’utiliser un pointeur de la classe de base pour stocker l’adresse d’objets dérivés. Contrainte Vous ne pouvez pas, avec un pointeur de base, appeler une fonction de la classe dérivée si cette fonction n’est pas virtuelle. Solution Convertir le pointeur de la classe de base vers un pointeur de la classe dérivée. Polygone_2D * p; Polygone_2D_convexe * q; ………. q = (Polygone_2D_convexe *) p; Problème Cette conversion fonctionne toujours même si elle n’a aucun sens. Si p contient l’adresse d’un objet Triangle_2D, il serait incorrect de le transformer en un pointeur de type Polygone_2D_convexe. Note:
55
Chapitre VII - Techniques plus avancées à travers le concept de classe55 Conversion de pointeurs d’objets dynamic_cast permet de convertir un pointeur d’une classe de base vers une de ses classes dérivées en autant que cette conversion est valide. q = dynamic_cast (p); Si la conversion est valide, q reçoit l’adresse convertie; autrement, la constante NULL.
56
Chapitre VII - Techniques plus avancées à travers le concept de classe56 Espaces de noms (« namespaces ») Tout programme que vous développez utilise un espace de nom. On ne peut pas définir 2 éléments du langage qui portent le même nom. Cette contrainte est difficile à respecter dans le cas d’une application complexe développée par plusieurs programmeurs et utilisant plusieurs librairies de classes. Solution proposée par C++: Définir plusieurs espaces de noms dans une même application pour éviter les conflits de noms. On peut intégrer à un namespace, des variables globales, des fonctions, des classes, des fonctions membres, des structures, des nouveaux types de données et d’autres espaces de noms. Pour manipuler ces sous-éléments, on peut utiliser soit l’opérateur ::, soit se servir de l’opérateur using.
57
Chapitre VII - Techniques plus avancées à travers le concept de classe57 Espaces de noms (« namespaces ») Exemple: namespace Caissier { ……. float duree_moyenne_de_service; void Afficher() { ……… } ……. } namespace Quai_de_dechargement { ……. float duree_moyenne_de_service; void Afficher() { ……… } ……. } déf. semblable à une classe (le ; est absent) Accès aux éléments Caissier::duree_moyenne_de_service = 2.2; Quai_de_dechargement::Afficher(); using namespace Caissier; duree_moyenne_de_service = 2.2; Afficher();
58
58 Espace de noms std Cela permet d’utiliser des compilateurs compatibles avec les normes ANSI/ISO C++ car ce standard spécifie de nouveaux noms pour plusieurs anciens fichiers d’en-tête du C++, dont iostream.h. La plupart de ces nouvelles appellations n’utilisent plus l’extension.h. Exemple: #include using namespace std; int main() { cout << "Bienvenue au C++!\n" ; std::cout << "Bienvenue au C++!\n" ; return 0; } Permet d’utiliser la version courte de chaque nom inclus dans la bibliothèque standard C++ 2 façons d’utiliser les fonctionnalités des fichiers d’en-tête de la bibliothèque standard. Cela garantit que chaque fonctionnalité fournie par la bibliothèque standard C++ est unique.
59
59 Espaces de noms Attribuez à vos espaces de noms des noms longs et uniques : Universite_Laval_Ift_Systeme_de_gestion_E01. Servez-vous du dispositif d’alias pour créer des alias courts : namespace ULISE01 = Universite_Laval_Ift_Systeme_de_gestion_E01; Les programmeurs seront bien contents.
60
Chapitre VII - Techniques plus avancées à travers le concept de classe60 Utilisation de l’opérateur :: Rappel pour les fonctions membres pour accéder aux éléments d’un espace de nom. En plus pour désigner les variables globales ou les fonctions qui n’appartiennent pas aux classes, de même nom que des variables locales ou des fonctions membres. float Poids = 500; void Afficher(); {cout << " NomPoids " << "\n";} class cheval { protected: char Nom[20+1]; float Poids; public: void Afficher(); };
61
Chapitre VII - Techniques plus avancées à travers le concept de classe61 Utilisation de l’opérateur :: void cheval::Afficher() { Poids = ::Poids;// ::Poids désigne la variable globale Poids. ::Afficher();// Appel de la fonction Afficher() qui n’appartient à // aucune classe. cout << Nom << " " << Poids; }
62
Chapitre VII - Techniques plus avancées à travers le concept de classe62 Surcharge d’opérateurs Nous avons utilisé les opérateurs du langage C++ (+, -, =, ==, etc.) pour manipuler les types primaires du langage (short, int, float, etc.). Ces opérateurs ne peuvent pas être employés nativement avec les classes que l’on définit. On peut donner une signification à ces opérateurs pour les nouvelles classes en utilisant la surcharge d’opérateurs. Note: La surcharge d’un opérateur s’applique à un type particulier de donnée. On doit définir cette surcharge pour toutes les classes concernées et tous les types d’argument (pointeur, référence, valeur) qui seront appliqués à cet opérateur. Doit-on préférer la surcharge d’opérateurs à la définition de fonctions membres? Avis partagés But :Permettre aux opérateurs du C++ de travailler avec des objets de classes. Cela permettra une extension simple et naturelle du C++.
63
63 Surcharge d’opérateurs Exemples en C++ : L’opérateur << a plusieurs usages :opérateur d’insertion de flux opérateur binaire de décalage à gauche. L’opérateur >> a plusieurs usages :opérateur d’extraction de flux opérateur binaire de décalage à droite. Les opérateurs + et – remplissent différentes fonctions : arithmétique entière arithmétique de nombres à virgule flottante arithmétique de pointeurs. Définition : correspond à la définition classique de fonction, avec un en-tête et un corps, avec cette petite différence que l’on baptise la fonction du mot-clé operator, suivi du symbole de l’opérateur surchargé.
64
64 Surcharge d’opérateurs Pour utiliser un opérateur sur les objets d’une classe, on doit surcharger cet opérateur. 2 exceptions : 2 L’opérateur d’affectation ( = ) est disponible pour toute classe, sans surcharge explicite ce qui n’est pas le cas des opérateurs +=, -=, *= et /=. Le comportement par défaut de l’opérateur d’affectation consiste en une affectation membre à membre pour les données membres de la classe. DANGER : Dans le cas des membres pointeurs. On utilise pour la gestion dynamique de la mémoire une surcharge explicite de l’opérateur d’affectation. Autrement, la surcharge implicite suffit. 1 L’opérateur d’adresse & est disponible pour toute classe, sans surcharge explicite. Il renvoie l’adresse de l’objet en mémoire. L’opérateur d’adresse peut également être surchargé.
65
65 Surcharge implicite de l’opérateur d’affectation = #include class Date { private :int jour;int mois;int annee; public :Date(int j =1, int m = 1, int a = 1990); void afficher(); }; Date::Date(int j, int m, int a) {jour = j;mois = m;annee = a;} void Date::afficher() {cout << jour << "-" << mois << "-" << annee;}
66
66 Surcharge implicite de l’opérateur d’affectation = void main() { Date date1(4, 7, 1993), date2; cout << "date1 = "; date1.afficher(); cout << "\ndate2 = "; date2.afficher(); date2 = date1; cout << "\n\nAprès copie au membre par défaut, date2 = "; date2.afficher(); cout << endl; } date1 = 4-7-1993 date2 = 1-1-1990 Après copie au membre par défaut, date2 = 4-7-1993
67
67 Surcharge implicite de l’opérateur d’affectation = Appel d’un constructeur depuis un constructeur : class Employe { public :Employe(); Employe(string Nom_employe, double Salaire_initial, int Heure_arrivee, int Heure_depart);... private :string nom;double salaire; Temps arrive;Temps depart; }; Employe::Employe(string Nom_employe, double Salaire_initial, int Heure_arrivee, int Heure_depart) { nom = Nom_employe;salaire = Salaire_initial; arrive = Temps(Heure_arrivee, 0, 0); depart = Temps(Heure_depart, 0, 0); } Appel au constructeur de la classe Temps m s
68
68 Surcharge d’opérateurs Possibilités et limites: On doit se limiter aux opérateurs existants, en conservant leur "pluralité " (unaire, binaire). C++ ne permet pas la création de nouveaux opérateurs. Les opérateurs ainsi surdéfinis gardent leur priorité et associativité habituelles. Un opérateur surdéfini doit toujours posséder une opérande de type classe. Il doit s’agir: soit d’une fonction membre, auquel cas elle dispose obligatoirement d’un argument implicite du type de sa classe (this); soit d’une fonction amie ou non possédant au moins un argument de type classe. Cela rend notamment les expressions mathématiques plus faciles à lire. On ne peut donc modifier la signification des opérateurs usuels : transformer le + en * lorsqu’il est appliqué à 2 entiers. À l’exception de l’opérateur d’appel de fonction, le # d’arguments exigé pour chaque opérateur est fixe et ne peut être modifié.
69
69 Surcharge d’opérateurs On définit les opérateurs comme méthode de classe autant que possible. Dans ce cas, le 1er argument doit représenter un objet de la classe ou une référence à un objet de la classe associée à l’opérateur. Exception : on définit une fonction à l’extérieur de la classe lorsque le type du 1er argument de l’opérateur n’est pas la classe. Ex.: v = 5 * w;où v et w sont des objets d’une classe Vecteur. Exception: Lors de la surcharge des opérateurs (), [], -> ou de tout autre opérateur d’affectation, la fonction de surcharge d’opérateur doit être déclarée comme membre de la classe. Ex.: L’opérateur << surchargé doit posséder un opérande gauche de type ostream & (par ex. cout << objetClasse); l’opérateur doit donc être une fonction non membre. Ces fonctions non membres surchargées peuvent être déclarées amies pour des raisons liées aux performances (permettant l’accès direct à des données privées).
70
70 Surcharge d’opérateurs Une autre raison de choisir une fonction non membre dans le contexte de la surcharge d’un opérateur est de permettre à cet opérateur de devenir commutatif. Exemple : Soit une variable nombre de type long int et un objet grosNombre de la classe Grand_Nombre (cette classe offrant des entiers aussi longs que l’on veut), l’opérateur + doit produire un objet de la classe Grand_Nombre représentant la somme d’un Grand_Nombre et d’un long int (grosNombre + nombre) ou représentant la somme d’un long int et d’un Grand_Nombre (nombre + grosNombre). Opérateur surchargé comme fonction membre Fonction amie non membre +
71
71 Surcharge d’opérateurs Liste complète des opérateurs qui peuvent être surchargés : +-*/%^&|~! =<>+=-=*=/=%=^=&= |= > >===!= =&& ||++--->->*[](),new new[]deletedelete[] Les formes unaire et binaire des opérateurs +, -, * et / peuvent être surchargées. On ne peut surcharger les opérateurs suivants :..*::?:sizeof et on ne peut créer de nouveaux opérateurs :|x|y := xy = x **2 Il faut éviter les surprises et la confusion qui pourrait rendre la lecture d’un programme obscure et difficile. Rendez intuitive l’utilisation des opérateurs. Ex.: Définir l’opérateur + comme faisant une soustraction : à éviter.
72
72 Surcharge d’opérateurs Incrément et décrément : Le compilateur ne transformera pas automatiquement v += w; en v = v + w. On doit définir les opérateurs +, = et +=. De façon similaire, x++; n’est pas nécessairement équivalent à x = x + 1; La règle est donc de définir un ensemble complet d’opérateurs qui ont des interactions naturelles entre eux : a = a + bdoit être == à a += b a – b doit être == à a + (-b) - ( - a)==+ a==a Pour assurer la cohérence entre les opérateurs apparentés, utilisez-en un pour implanter les autres. Exemple : Utilisez un opérateur + surchargé pour implanter un opérateur += surchargé.
73
73 Surcharge d’opérateurs unaires : exemples 1er cas : une fonction membre non static sans argument class Chaine { private :…. public : bool operator!() const; …… }; Soit s un objet ou une référence à un objet de la classe, alors !s génère un appel à cette fonction. Vérifier si un objet est vide ou non.
74
74 Surcharge d’opérateurs unaires : exemples 2ième cas : une fonction non membre recevant un argument class Chaine { friend bool operator!( const Chaine & ); …… }; Soit s un objet ou une référence à un objet de la classe, alors !s génère un appel à cette fonction amie non membre de la classe. Vérifier si un objet est vide ou non. Si l’argument représente un objet, cela nécessite une copie de cet objet afin que les effets de bord de la fonction ne soient pas appliqués à l’objet d’origine. Si l’argument représente une référence à un objet, aucune copie de l’objet d’origine n’est réalisée et tous les effets de bord de cette fonction sont alors appliqués à l’objet d’origine.
75
75 Surcharge d’opérateurs unaires : exemples 2ième cas : une fonction non membre recevant un argument #include class Chaine { private : int m; public :Chaine(int n =0); friend bool operator!(const Chaine & c); }; Chaine::Chaine(int n){m = n;} bool operator!(const Chaine & c){return (c.m == 0);} void main() { Chaine d(2);Chaine e;Chaine &f = e; cout << (int)!d << (int)!f << endl; }
76
76 Surcharge d’opérateurs binaires : exemples 1er cas : une fonction membre non static avec un argument class Chaine { private :…. public : const Chaine & operator += (const Chaine & C); …… }; Si y et z représentent des objets de la classe Chaine, alors y += z devient traité comme si l’on avait écrit y.operator += (z) en invoquant la fonction membre operator +=.
77
77 Surcharge d’opérateurs binaires : exemples 2ième cas :une fonction non membre avec 2 arguments (un de ces arguments doit être un objet ou une référence à un objet de la classe) class Chaine { private :…. public : friend const Chaine & operator += (Chaine & C, const Chaine & D); …… }; Si y et z représentent des objets de la classe Chaine, alors y += z est traité comme si l’on avait écrit l’appel operator += (y,z) dans le programme en invoquant la fonction friend non membre operator +=.
78
78 Étude de cas : classe « Vecteur » En C++, le concept de tableau a ses limites : Le C++ ne vérifie pas si les indices aboutissent en dehors de la plage du tableau. Les tableaux de taille n doivent numéroter leurs éléments 0, …, n-1; les alternatives de plages d’indices ne sont pas permises. Chaque élément d’un tableau doit être lu ou écrit individuellement. On ne peut comparer globalement deux tableaux puisque les noms des tableaux ne sont que des pointeurs vers les emplacements de mémoire où les tableaux débutent. Lorsqu’un tableau est transmis à une fonction d’usage général, conçue pour manipuler des tableaux de toute taille, la taille de ce dernier doit être passée en argument supplémentaire. Un tableau ne peut être affecté à un autre avec les opérateurs d’affectation. etc. On peut mettre en place de telles caractéristiques grâce à la surcharge d’opérateurs.
79
Chapitre VII - Techniques plus avancées à travers le concept de classe79 Exemple : classe « Vecteur » enum type_de_composante {entier, reel, reel_double}; union type_numerique { int composante_entiere;float composante_reelle; double composante_reelle_double; }; class Vecteur { /*Spécification fonctionnelle de la classe " Vecteur " Composantes :Chaque composante est une valeur numérique. Les composantes sont de même type. Structure :Il existe une relation linéaire (structure) entre les composantes d'un vecteur. */
80
Chapitre VII - Techniques plus avancées à travers le concept de classe80 Exemple : classe « Vecteur » protected: type_de_composante statut;/* Type des composantes */ int n;/* Longueur du vecteur */ type_numerique*v; /* Pointeur vers le vecteur de composante */ public: Vecteur(type_de_composante t = reel, int L = 3); /*Constructeur permettant de créer un vecteur nul de longueur L > 0 (3 par défaut) dont les composantes sont des valeurs numériques (réel par défaut). Pré -L > 0. Post -Le vecteur de longueur L est le vecteur nul.*/
81
Chapitre VII - Techniques plus avancées à travers le concept de classe81 Exemple : classe « Vecteur » type_numerique & operator [] (int i); /*Donne accès à la ième composante du vecteur. Pré -Le vecteur a déjà été créé et 1 <= i <= longueur du vecteur. Post - Donne accès à la ième composante du vecteur.*/ int operator ~ (); int dim(); friend int dim(Vecteur P); /*Donne accès à la longueur du vecteur. Pré -Le vecteur a déjà été créé. Post -Retourne la longueur du vecteur.*/ On peut effectuer un test de plage contrairement à C++.
82
Chapitre VII - Techniques plus avancées à travers le concept de classe82 Exemple : classe « Vecteur » void Detruire_vecteur(); /*Permet de détruire le vecteur et libérer l'espace correspondante Pré -Le vecteur a déjà été créé. Post -Le vecteur n'existe plus.*/ type_numerique operator * (Vecteur P); /*Fournit le produit scalaire du vecteur courant avec le vecteur P où les composantes des 2 vecteurs sont de même type. Pré -Le vecteur courant et le vecteur P ont déjà été créés et sont de même longueur. Les éléments sont de même type. Post -Retourne le produit scalaire des 2 vecteurs.*/
83
Chapitre VII - Techniques plus avancées à travers le concept de classe83 Exemple : classe « Vecteur » void operator * (type_numerique Lambda); /*Multiplie chaque composante du vecteur par le scalaire "Lambda". Pré -Le vecteur a déjà été créé. Le scalaire et les composantes du vecteur sont de même type. Post -Chaque composante du vecteur a été multipliée par "Lambda".*/
84
Chapitre VII - Techniques plus avancées à travers le concept de classe84 Exemple : classe « Vecteur » void operator += (Vecteur P); /*Permet d'additionner le vecteur P au vecteur courant. Les composantes des 2 vecteurs sont de même type. Pré -Le vecteur courant et le vecteur P ont déjà été créés et sont de même longueur. Post -Additionne P au vecteur courant.*/ void operator -= (Vecteur P); /*Permet de soustraire le vecteur P du vecteur courant. Les composantes des 2 vecteurs sont de même type. Pré -Le vecteur courant et le vecteur P ont déjà été créés et sont de même longueur. Post -Soustrait P du vecteur courant.*/ };
85
Chapitre VII - Techniques plus avancées à travers le concept de classe85 Exemple : classe « Vecteur » #include #include "Vecteur.h" Vecteur::Vecteur(type_de_composante t, int L): n(L), statut(t) { int i;v = new type_numerique[L]; switch (statut) { case entier: for (i = 0; i < n; i++) (v + i) -> composante_entiere = 0; break; case reel: ……. }
86
Chapitre VII - Techniques plus avancées à travers le concept de classe86 Exemple : classe « Vecteur » type_numerique & Vecteur::operator [] (int i) { return *(v + i - 1); } int Vecteur::operator ~ () { return n; } void Vecteur::Detruire_vecteur() { delete v; } int Vecteur::dim() { return n; } int dim(Vecteur P) { return P.n; } ou encore Utilité?
87
Chapitre VII - Techniques plus avancées à travers le concept de classe87 Exemple : classe « Vecteur » type_numerique Vecteur::operator * (Vecteur P) {int i;type_numerique somme; switch (statut) {case entier: somme.composante_entiere = 0; for (i = 0; i < n; i++) { somme.composante_entiere += P[i+1].composante_entiere * (*(v + i)).composante_entiere; }break; case reel:…. }return somme; }
88
Chapitre VII - Techniques plus avancées à travers le concept de classe88 Exemple : classe « Vecteur » void Vecteur::operator * (type_numerique Lambda) {int i = 0; switch (statut) { case entier: for (i = 0; i < n; i++) (*(v+i)).composante_entiere = (*(v+i)).composante_entiere * Lambda.composante_entiere; break; case reel:…. }
89
Chapitre VII - Techniques plus avancées à travers le concept de classe89 Exemple : classe « Vecteur » void Vecteur::operator += (Vecteur P) {int i; switch (statut) {case entier: for (i = 0; i < n; i++) (*(v+i)).composante_entiere += P[i+1].composante_entiere; break; case reel:…. }
90
Chapitre VII - Techniques plus avancées à travers le concept de classe90 Exemple : classe « Vecteur » void Vecteur::operator -= (Vecteur P) { int i; switch (statut) {case entier: for (i = 0; i < n; i++) (*(v+i)).composante_entiere -= P[i+1].composante_entiere; break; case reel:…. }
91
Chapitre VII - Techniques plus avancées à travers le concept de classe91 Exemple : classe « Vecteur » #include #include "Vecteur.h" void main() {int i;int L = 3; Vecteur P(reel_double);Vecteur Q(reel_double, L); type_numerique Resultat; for (int i=1; i <= L; i++) { Resultat.composante_reelle_double = (double) i; P[i] = Resultat; Resultat.composante_reelle_double = 0.1; Q[i] = Resultat; }
92
Chapitre VII - Techniques plus avancées à travers le concept de classe92 Exemple : classe « Vecteur » for (i=1; i <= L; i++) cout<< P[i].composante_reelle_double << " ; " << Q[i].composante_reelle_double << " ; " << endl; cout << "Longueur du vecteur : " << ~P; cout << "Longueur du vecteur: " << P.dim(); cout << "Longueur du vecteur: " << dim(P); Resultat = P * Q; cout << "Produit scalaire : " << Resultat.composante_reelle_double; Resultat.composante_reelle_double = 0.2; Q * Resultat; Resultat = Q * P; cout << "Produit scalaire : " << Resultat.composante_reelle_double;
93
Chapitre VII - Techniques plus avancées à travers le concept de classe93 Exemple : classe « Vecteur » P += Q; Resultat = Q * P; cout << "Produit scalaire : " << Resultat.composante_reelle_double; P -= P; Resultat = P * Q; cout << "Produit scalaire : " << Resultat.composante_reelle_double; }
94
Chapitre VII - Techniques plus avancées à travers le concept de classe94 Constructeur de copie, destructeur et affectation Note : Les 3 forment un tout : class chaine { private: int * suite; int m; void copie(chaine & c); public: chaine(int n); int longueur(); int & operator [] (int i); chaine(chaine & c); chaine & operator = (chaine & c); ~chaine(); }; Copie la valeur de l’argument.
95
95 Constructeur de copie, destructeur et affectation chaine::chaine(int n) { m = n; suite = new int[n]; for (int i = 0; i < n; i++) suite[i] = 0; } int chaine::longueur() {return m;} int & chaine::operator [] (int i) {return suite[i];} void chaine::copie(chaine & c) { m = c.longueur(); suite = new int[m]; for (int i = 0; i < m; i++) suite[i] = c[i]; } chaine & chaine::operator = (chaine & c) { delete [] suite; copie(c); return *this; } chaine::chaine(chaine & c) { copie(c); } chaine::~chaine() { delete [] suite; }
96
Chapitre VII - Techniques plus avancées à travers le concept de classe96 Surcharge d’opérateurs d’incrémentation et de décrémentation Le plus souvent défini comme fonction membre. Il existe 2 versions de chacun : une version préfixe : Effectue la modification avant la production du résultat. ++x une version suffixe : Renvoie la valeur originale avant la modification. x++ Note :Pour distinguer entre les 2 versions, C++ ajoute un argument supplémentaire à la version suffixe. La valeur de cet argument n’est pas employée.
97
97 Surcharge d’opérateurs d’incrémentation et de décrémentation Exemple : class Fraction {... Fraction & operator ++ ();// Version préfixe Fraction operator ++ (int inutilise);// Version suffixe... }; Fraction Fraction::operator ++ (int inutilise) {Fraction clone(numerateur, denominateur); numerateur += denominateur; return clone;} Fraction & Fraction::operator ++ () { numerateur += denominateur; return * this; } Ne pas renvoyer de référence vers une variable locale. Note : Éviter les expressions ambigües :int i = 5;int x = i + ++i; (x contient 12).
98
98 Surcharge d’opérateurs de conversion Il existe en C++ une vaste gamme de conversions implicites : 6 * 3.1415926. Lorsqu’il s’agit de nouvelles classes définies par l’utilisateur, vous devez considérer 2 catégories de conversion : La conversion d’un type vers la nouvelle classe définie par l’utilisateur. Ces conversions implicites sont gérées à l’aide de constructeurs. #include class Fraction { public: int num; int den; Fraction(int n, int d); Fraction & operator + (const Fraction & F) const; }; Exemple :
99
99 Surcharge d’opérateurs de conversion Fraction::Fraction(int n, int d) { num = n;den = d; } Fraction & Fraction::operator + (const Fraction & F) const { Fraction * G = new Fraction(num * F.den + den * F.num, den * F.den); return *G; } void main() { Fraction F(2, 4);Fraction G(1, 1); Fraction H = F + G; cout << H.num << " " << H.den << endl; Fraction J = F + 1; cout << J.num << " " << J.den << endl; } Erreur : Surcharge de l’opérateur + avec un entier non définie. Absence de constructeur avec un argument entier.
100
100 Surcharge d’opérateurs de conversion Ajoutons à la classe Fraction le constructeur suivant : Fraction(int n); … Fraction::Fraction(int n) { num = n; den = 1; } dans le fichier Fraction.h dans le fichier Fraction.cpp Fraction J = F + 1; est converti comme si nous avions écrit : Fraction J = F + Fraction(1); Ajoutons encore la surcharge de l’opérateur + suivante : Fraction & Fraction::operator + (int n) const { return (*this) + Fraction(n); } Absence de conversion implicite: cette fonction est appelée.
101
101 Surcharge d’opérateurs de conversion La conversion depuis la nouvelle classe définie par l’utilisateur en un autre type. Imaginons maintenant que l’on remplace dans la fonction principale : float J = F + 1; cout << J << endl; Fraction J = F + 1; cout << J.num << " " << J.den << endl; par Cela occasionnerait une erreur car il n’y a pas de conversion possible depuis une référence à un objet de la classe Fraction à un type float. Pour remédier à ceci, nous définissons un opérateur de conversion qui emploie un type comme nom d’opérateur.
102
102 Surcharge d’opérateurs de conversion operator float() const;... Fraction::operator float() const { return ((float)num) / den; } dans le fichier Fraction.h dans le fichier Fraction.cpp Un opérateur de conversion ne possède pas d’argument et ne spécifie pas de type de renvoi. Note :
103
103 Surcharge d’opérateurs de conversion C++ est incapable d’effectuer plus d’un niveau de conversion de type défini par l’utilisateur lorsqu’il tente d’interpréter une fonction surchargée. Note : Exemple : Définissons un type de données Complexe où sont prévues - l’addition de 2 complexes, - l’addition d’une valeur de type float à un complexe. Complexe c(2, 3); Fraction a(1, 3); c = c + a;// Erreur. Cela exigerait les conversions : Fraction à float et float à Complexe. Sol n :définir explicitement un opérateur Complexe dans la classe Fraction ou ajouter à la classe Complexe un constructeur acceptant une valeur Fraction.
104
104 Surcharge d’opérateurs de conversion Note :Mot clé explicit Il est parfois souhaitable de disposer d’un constructeur à un argument employé uniquement pour la création de nouveaux objets et non pour offrir une règle de conversion implicite d’un type à un autre. C++ propose à cette fin le mot clé explicit. Exemple :class Polynome {... explicit Polynome(int n); Polynome & operator = (Polynome P);... };
105
105 Surcharge d’opérateurs de conversion En l’absence du mot clé explicit, Polynome P(5); P = 2;// On voulait écrire P[0] = 2; // Un polynôme de degré 2 est créé car il y a conversion // implicite de int à Polynome. cout << P[0] << P[1] << P[2];
106
Chapitre VII - Techniques plus avancées à travers le concept de classe106 Les foncteurs L’opérateur d’appel de fonction () peut être surchargé mais uniquement comme fonction membre de classe. Il s’agit d’un opérateur binaire: une expression de la forme f(x, y) est interprétée comme f.operator()(x, y) Foncteur : le nom d’un objet appartenant à une classe où l’opérateur () est surchargé. Une surcharge de l’opérateur () a un prototype de la forme: type operator() (paramètres) {……….} Terminologie introduite par la bibliothèque standard (STL). Voir chap. 12
107
Chapitre VII - Techniques plus avancées à travers le concept de classe107 Les foncteurs #include class less_equal { public: bool operator()(int i, int j){ return i <= j; } bool operator()(float u, float v){ return u <= v; } bool operator()(char ca, char cb) { return ca <= cb; } bool operator()(char *ca, char *cb) { return strcmp(ca, cb) <= 0; } };
108
Chapitre VII - Techniques plus avancées à travers le concept de classe108 Les foncteurs void main() { less_equal cmp; cout << "(plu <= me) = " << cmp("plu", "me") << endl; cout << "(245 <= 399) = " << cmp(245, 399) << endl; }
109
109 Les foncteurs L’opérateur d’appel de fonction permet de créer des objets à l’aide de classes mais il est employé comme s’il s’agissait de fonctions. Exemple : #include // Fonction ne recevant pas d'argument qui retourne un entier aléatoire entre 1 et 100. int aleatoire_100() { return 1 + rand() % 100; } // Fonction ne recevant pas d'argument qui retourne un entier aléatoire entre a et b // où a et b sont des valeurs inconnues jusqu’à l’exécution. Entier aléatoire.
110
Chapitre VII - Techniques plus avancées à travers le concept de classe110 Les foncteurs class AleatoireInt { public: AleatoireInt(int ia, int ib); int operator()(); private: int a, b; }; AleatoireInt::AleatoireInt(int ia, int ib) :a(ia), b(ib) { } int AleatoireInt::operator()() { return a + rand() % (b – a + 1); }
111
Chapitre VII - Techniques plus avancées à travers le concept de classe111 Les foncteurs AleatoireInt a(7, 25); cout << "une valeur aléatoire : " << a() << "puis une autre " << a() << endl; Une fois créé, l’objet peut être invoqué comme pour une fonction : Cet opérateur d’appel de fonction est unique, en ce que c’est le seul opérateur de C++ pour lequel le nombre d’arguments n’est pas fixe. int operator() (int nb);int AleatoireInt::operator()(int nb) { return a + rand() % (nb – a + 1); } int operator() (int na, int nb);int AleatoireInt::operator()(int na, int nb) { return na + rand() % (nb – na + 1); }
112
Chapitre VII - Techniques plus avancées à travers le concept de classe112 Les foncteurs La fonction sera sélectionnée en fonction du # d’arguments fournis : AleatoireInt r(3, 7); cout << "valeur aleatoire entre 3 et 7 " << r() << endl; cout << "valeur aleatoire entre 3 et 10 " << r(10) << endl; cout << "valeur aleatoire entre 23 et 30" << r(23,30) << endl; FIN Nous reverrons les foncteurs en étudiant les algorithmes génériques de la librairie STL.
Présentations similaires
© 2024 SlidePlayer.fr Inc.
All rights reserved.