ENIG GCR1 Programmation en C++ Amira Mekki Janvier 2009.

Slides:



Advertisements
Présentations similaires
La programmation orientée objet avec Java L3-MIAGE Plan
Advertisements

Premier programme en C :
Spécialisation/généralisation Héritage Polymorphisme.
Spécialisation/généralisation Héritage Polymorphisme
C.
Programmation Orientée Objet (POO)
FLSI602 Génie Informatique et Réseaux
Leçon 3 : Héritage IUP 2 Génie Informatique
Introduction à la POO: Les classes vs les objets
Chapitre III Héritage (début)
Principes de programmation (suite)
Structures de données linéaires
Programmation orientée objet
Algorithmique et Programmation
Langage Oriente Objet Cours 4.
Leçon 6 : Structures de données dynamiques IUP 2 Génie Informatique Méthode et Outils pour la Programmation Françoise Greffier.
Introduction au paradigme objet Concepts importants surcharge (overload) redéfinition (override) Définition d’une classe Définition des attributs.
© 2007 P. Van Roy. All rights reserved. FSAB1402: Informatique 2 Le Langage Java et les Exceptions Peter Van Roy Département dIngénierie Informatique,
C++ : classes Introduction aux Langages Orientés Objets
77 Utilisation des classes (suite). 7-2 Objectifs A la fin de ce cours, vous serez capables de : Définir des méthodes surchargées dans une classe Fournir.
Quest-ce quune classe dallocation? Une classe dallocation détermine la portée et la durée de vie dun objet ou dune fonction.
Langage Oriente Objet Cours 2.
Leçon 2 : Surcharge des opérateurs IUP 2 Génie Informatique Méthode et Outils pour la Programmation Françoise Greffier Université de Franche-Comté.
Les pointeurs Modes d’adressage de variables. Définition d’un pointeur. Opérateurs de base. Opérations élémentaires. Pointeurs et tableaux. Pointeurs et.
Structures de données IFT-2000
Structures de données IFT Abder Alikacem La classe string Département dinformatique et de génie logiciel Édition Septembre 2009 Département dinformatique.
66 Utilisation des classes et des objets. 6-2 Objectifs A la fin de ce cours, vous serez capables de : Créer de nouvelles classes à laide de Eclipse Utiliser.
Introduction au paradigme orienté-objet (suite)
1 IFT 6800 Atelier en Technologies dinformation Le langage de programmation Java chapitre 3 : Classes et Objects.
Types de données abstrait et mécanismes d'encapsulation
Leçon 1 : notion dobjet IUP Génie Informatique Besançon Méthode et Outils pour la Programmation Françoise Greffier Université de Franche-Comté.
Structures de données IFT-10541
Structures de données IFT-2000 Abder Alikacem Retour sur les listes ordonnées Département dinformatique et de génie logiciel Édition Septembre 2009.
Procédures et fonctions
Plan cours La notion de pointeur et d’adresse mémoire.
Héritage Licence Informatique Besançon Méthode et Outils pour la Programmation Françoise Greffier.
Travaux Pratiques Représentation des connaissances
La notion de type revisitée en POO
11/04/ L'héritage Cours 7 Cours 7.
7ième Classe (Mardi, 24 novembre) CSI2572. Devoir 3 ?
Variables et accès en Java. Déclaration des variables final transient static private Printer hp; transient => ne doivent pas être sérialisées volatile.
Créer des packages.
1 Structures des données. 2  Le tableau permettait de désigner sous un seul nom un ensemble de valeurs de même type, chacune d'entre elles étant repérée.
Master 1 SIGLIS Java Lecteur Stéphane Tallard Les erreurs communes en Java.
12/04/ Le polymorphisme Cours 8 Cours 8.
Cours C++ Fonctions Surcharge d’opérateurs Passage d’arguments
© 2005 P. Van Roy. All rights reserved. FSAB1402: Informatique 2 Le Langage Java Peter Van Roy Département d’Ingénierie Informatique, UCL
Tutorat en bio-informatique
Introduction à la programmation objet en C++
5ième Classe (Mercredi, 19 octobre) Prog CSI2572.
Tutorat en bio-informatique Le 14 novembre Au programme… Les objets –Propriétés (attributs) –Constructeurs –Méthodes.
C++ L’HERITAGE Fayçal BRAÏKI DUT INFORMATIQUE.
Les classes et les objets Les données finales class A { … private final int n = 20 ; // la valeur de n est définie dans sa déclaration … } class A { public.
Les surcharges d'opérateurs
Les classes Introduction aux Langages Orientés Objets
Cours 4 (14 octobre) Héritage. Chapitre III Héritage.
Héritage Conception par Objet et programmation Java
Conception de Programmes - IUT de Paris - 1ère année Quelques éléments du langage C++ Les références La surcharge de fonctions Les fonctions «
Campus-Booster ID : Copyright © SUPINFO. All rights reserved La programmation objet, un fondement de la programmation évènementielle.
Langage de Programmation Orientée Objet : C++
Exception Handling "Unfortunately, it's almost accepted practice to ignore error conditions, as if we're in a state of denial about errors." Bruce Eckel.
Philippe Gandy - 15 septembre 2015 Basé sur les notes de cours de Daniel Morin et Roch Leclerc.
Conception de Programmes - IUT de Paris - 1ère année Les classes Introduction Déclaration d’une classe Utilisation d’une classe Définition des.
Master 1 SIGLIS Jave Lecteur Stéphane Tallard Chapitre 5 – Correction TD.
Transcription de la présentation:

ENIG GCR1 Programmation en C++ Amira Mekki Janvier 2009

Entrées-sorties simples Cette section traite de l'utilisation simple des flux standard d'entrée-sortie, c'est-à-dire la manière de faire en C++ les opérations qu'on fait habituellement en C avec les fonctions printf et scanf. Un programme qui utilise les flux standard d'entrée-sortie doit comporter la directive #include <iostream.h> ou bien, si vous utilisez un compilateur récent et que vous suivez de près les recommandations de la norme4 : #include <iostream> using namespace std;

Entrées-sorties simples Les flux d'entrée-sortie sont représentés dans les programmes par les trois variables, pré déclarées et pré initialisées, suivantes : Cin: le flux standard d'entrée Cout: le flux standard de sortie Cerr: le flux standard pour la sortie des messages d'erreur

Entrées-sorties simples Les écritures et lectures sur ces unités ne se font pas en appelant des fonctions, mais à l'aide des opérateurs << appelé opérateur d'injection ( injection de données dans un flux de sortie), >> appelé opérateur d'extraction ( extraction de données d'un flux d'entrée).

Entrées-sorties simples cout << expression à écrire le résultat de cette expression est l'objet cout lui-même. On peut donc lui injecter une autre donnée, puis encore une, etc. : cout << expression << expression << expression

Un exemple L’inévitable hello world: #include <iostream> using namespace std ; void main ( ) { cout << ” hello world ! ” << endl ; }

Exemple : programme C++ simple /* texte source (compt.cpp) d'un programme comptant le nombre de lignes du fichier texte lu sur l'entrée standard (utilisation des classes d'E/S standard*/ #include <iostream> // lecture des interfaces des classes d'E/S using namespace std ; Void main() //programme principal = fonction sans arguments { char carc; int nombreligne=0; while(cin.get(carac)!='# ') // tant que l'on n'a pas lu tout le texte à // partir du //flot d'entrée standard "cin" { if (carac=='\n') //si c'est une fin de ligne, on ++nombreligne; // incrémente le nombre de ligne } cout<<nombreligne<<"lignes\n"; //affichage du résultat sur le flot de sortie standard "cout"

Entrées / Sorties (I) C C++ #include <stdio.h> #include <iostream> #include <fstream> #include <strstream> printf --> standard output scanf <-- standard input fprintf --> FILE* fscanf <-- FILE* cout --> standard output cerr --> standard error output cin <-- standard input ofstream --> output file ifstream <-- input file ostream& operator<< istream& operator>>

Allocation mémoire C C++ int* var = new int( value ); int* var = (int*)malloc(sizeof(int)); *var = value; int* array = (int*)malloc(n*sizeof(int)); C++ int* var = new int( value ); int* array = new int[10]; En C++, les allocations sont typées.

Désallocation mémoire int* var = (int*)malloc(sizeof(int)); int* array = (int*)malloc(n*sizeof(int)); … free( var ); free( array ); C++ int* var = new int(); int* array = new int[10]; delete var; delete [] array;

Définition des classes Tous les membres d'une classe doivent être au moins déclarés à l'intérieur de la formule class nom { //les membres déclarés ici sont privés public: //les membres déclarés ici sont publics private: //etc. }; qui constitue la déclaration de la classe.

Les expressions public: et private: peuvent apparaître un nombre quelconque de fois dans une classe. Les membres déclarés après private: (resp. public:) sont privés (resp. publics) jusqu‘à la fin de la classe, ou jusqu‘à la rencontre d'une expression public: (resp. private:). Un membre public d'une classe peut être accédé partout où il est visible ; un membre privé ne peut être accédé que depuis une fonction membre de la classe

Définition des classes Exemple class Point { public: void afficher() { cout << '(' << x << ',' << y << ')'; } void placer(int a, int b) { //validation des valeurs de a et b; x = a; y = b; private: int x, y; };

Accès aux membres Accès aux membres d'un objet On accède aux membres des objets en C++ comme on accède aux membres des structures en C. Par exemple, à la suite de la définition de la classe Point donnée précédemment on peut déclarer des variables de cette classe en écrivant : Point a, b, *pt; // deux points et un pointeur de point

Accès aux membres Accès aux membres d'un objet Dans un contexte où le droit de faire un tel accès est acquis l'accès aux membres du point a s‘écrit : a.x = 0; // un accès bien écrit au membre x du point a dans le cas ou x est public d = a. placer(10,20); // un appel bien écrit de la fonction distance de l'objet a

Si on suppose que le pointeur pt a été initialisé, par exemple par une expression telle que pt = new Point; // allocation dynamique d'un point alors des accès analogues aux précédents s‘écrivent : pt->x = 0; // un accès bien écrit au membre x du point pointé par pt d = pt-> placer(10,20); // un appel de la fonction distance de l'objet pointé par pt

class Point { public: void afficher() { cout << '(' << x << ',' << y << ')'; } void placer(int a, int b) { //validation des valeurs de a et b; x = a; y = b; double distance(Point autrePoint) { int dx = x - autrePoint.x; int dy = y - autrePoint.y; return sqrt(dx * dx + dy * dy); private: int x, y; };

Définition des classes Cependant, dans le cas des fonctions, aussi bien publiques que privées, on peut se limiter à n‘écrire que leur en-tête à l'intérieur de la classe et définir le corps ailleurs, plus loin dans le même fichier ou bien dans un autre fichier. Il faut alors un moyen pour indiquer qu'une définition de fonction, écrite en dehors de toute classe, est en réalité la définition d'une fonction membre d'une classe. Ce moyen est l'opérateur de résolution de portée, dont la syntaxe est NomDeClasse::

Définition des classes Par exemple, voici notre classe Point avec la fonction distance définie séparément : class Point { public: ... double distance(Point autrePoint); } Il faut alors, plus loin dans le même fichier ou bien dans un autre fichier, donner la définition de la fonction promise dans la classe Point. Cela s‘écrit : double Point::distance(Point autrePoint) { int dx = x - autrePoint.x; int dy = y - autrePoint.y; return sqrt(dx * dx + dy * dy); }; Point.h Point.cpp

L'interface de la classe (« Point.h") contient les déclarations des méthodes de la classe. (indispensable pour le client) Les déclarations des attributs (en général, intitules au client donc inaccessibles) La réalisation de la classe (« Point.cpp") contient : Les définitions des méthodes qui n'ont été que déclarées dans l'interface de la classe.

EXEMPLE point.cpp Point.h #include "Point.H" #include <iostream> using namespace std ; #include <math.h> void Point::Afficher() { cout<< "( " << x<<","<<y<<")"<<endl; }; void Point :: placer(int a, int b) {x = a; y = b; double Point::distance(Point autrePoint) { int dx = x - autrePoint.x; int dy = y - autrePoint.y; return sqrt(dx * dx + dy * dy); class Point { public: void Afficher (); void placer(int a, int b); double distance(Point autrePoint); private : int x,y; };

Test des méthodes de la classe Point MainPoint.cpp #include "Point.H" #include <iostream> using namespace std ; void main ( ) { //declaration Point pt1; Point pt2; //Affectation des valeurs pour les deux points pt1.placer(1,1); pt2.placer(5,10); //Affichage des coordonnées pour les points pt1.Afficher(); pt2.Afficher(); // distance entre deux points double d=pt2.distance(pt1); cout <<" la distance entre les deux points est "<< d <<endl; }

Méthodes particulière : constructeur d'objet Un constructeur d'une classe est une fonction membre spéciale qui : a le même nom que la classe n'indique pas de type de retour ne contient pas d'instruction return Le rôle d'un constructeur est d'initialiser un objet, notamment en donnant des valeurs à ses données membres. Le constructeur n'a pas à s'occuper de trouver l'espace pour l'objet ; il est appelé (immédiatement) après que cet espace ait été obtenu, et cela quelle que soit la sorte d'allocation qui a été faite : statique, automatique ou dynamique, cela ne regarde pas le constructeur.

Exemple class Point { public: Point(int a, int b) { validation des valeurs de a et b x = a; y = b; } ... autres fonctions membres ... private: int x, y; };

Une classe peut posséder plusieurs constructeurs, qui doivent alors avoir des signatures différentes : class Point { public: Point(int a, int b) { validation de a et b x = a; y = b; } Point(int a) { validation de a x = a; y = 0; Point() { x = y = 0; ... private: int x, y; };

Point p(4,5); //appel de Point(4,5) Exemple: la classe Point à un constructeur Point(int,int) Point p(4,5); //appel de Point(4,5) Point * courant = new Point(3,2); //appel de Point(3,2) // Supposons que la classe PERSONNE ait un constructeur // PERSONNE(char *); PERSONNE patron("Dupont");

Point::Point(int a, int b) { //validation de a et b x = a; y = b; } point.cpp Point.h #include "Point.h" Point::Point(int a, int b) { //validation de a et b x = a; y = b; } class Point { public: Point(int a = 0, int b = 0); ... private: int x, y; };

Destructeurs De la même manière qu'il y a des choses à faire pour initialiser un objet qui commence à exister, il y a parfois des dispositions à prendre lorsqu'un objet va disparaître. Un destructeur est une fonction membre spéciale. Il a le même nom que la classe, précédé du caractère ~. Il n'a pas de paramètre, ni de type de retour. Il y a donc au plus un destructeur par classe.

Destructeurs Le destructeur d'une classe est appelé lorsqu'un objet de la classe est détruit, juste avant que la mémoire occupée par l'objet soit récupérée par le système. Exemple : class Point { ... ~Point () { delete [] label; } };

Synthèse du destructeur Si le programmeur n'a pas écrit de destructeur pour une classe, le compilateur en synthétise un, de la manière suivante : si la classe n'a ni objets membres ni classes de base, alors il s'agit du destructeur trivial qui consiste à ne rien faire, si la classe a des classes de base ou des objets membres, le destructeur synthétisé consiste à appeler les destructeurs des données membres et des classes de base, dans l'ordre inverse de l'appel des constructeurs correspondants.

EXERCICE1 « M. Holly Pierre est né en 1965 » Ecrire une classe Personne permettant de décrire complètement une personne, sachant que l'on souhaite avoir autant d'informations que dans la phrase suivante : « M. Holly Pierre est né en 1965 » Pour cela la classe doit contenir - un constructeur à la classe Personne. - une méthode de nom retourneInfos. Cette méthode doit afficher une chaîne de caractères similaire à la phrase donnée precedament Ecrire un programme Main de test de la classe qui déclare 3 variables de type Personne, crée 2 instances de personne pour les affecter dans les variables et affiche les informations les concernant

EXERCICE2 Écrire un programme qui calcule la note finale d'un étudiant. Le programme demandera le nom de l'étudiant puis sa note de partiel, sa note de l'exam final et son ensemble de notes de devoirs maisons. Il affichera ensuite le résultat tel que la note de partiel compte pour 20% , l'examen pour 40% et la moyenne des devoirs pour 40% de la note nale.

Paramètre d'une méthode Une méthode peut avoir comme paramètres (éventuellement préfixés par const) des valeurs ou références d'objet de type primitif, pointeur ou classe. Toute méthode a un argument implicite, qui est un pointeur sur l'objet sur lequel elle s'applique, de nom this On ajoute const à la fin de la déclaration de la méthode si elle ne modifie pas les attributs de l'objet auquel on l'applique.

Déclaration des paramètres On déclare les paramètres dans une liste, éventuellement vide, déclarations séparées par une virgule. Chaque déclaration est de la forme : [const]typeParam [&] nomParametre Le préfixe const signifie que l'argument n'est pas modifié par la méthode. & signifie que le passage est fait par référence, sinon le passage est effectué par valeur, c'est-à-dire par copie de la valeur de l'argument dans le paramètre local. Les noms des paramètres sont locaux à la méthode.

Exemple class Point { int x, y; public: int X() const { return x; } Avec la déclaration de la classe, les fonctions X et Y sont sécurisées : sur un objet constant elles ne permettent que la consultation, sur un objet non constant elles permettent la consultation et la modification : class Point { int x, y; public: int X() const { return x; } int Y() const { return y; } int & X() { return x; } int& Y() { return y; } ... }; const Point a(2, 3); Point b(4,5); int r; ... r = a.X(); // Oui a.X() = r; // ERREUR ( a.X() rend une valeur) r = b.X(); // Oui b.X() = r; // Oui ( b.X() rend une r¶ef¶erence)

Membres statiques Chaque objet d'une classe possède son propre exemplaire de chaque membre ordinaire (bientôt nous dirons membre non statique) de la classe : pour les données membres, cela signifie que de la mémoire nouvelle est allouée lors de la création de chaque objet ; pour les fonctions membres, cela veut dire qu'elles ne peuvent être appelées qu'en association avec un objet (on n'appelle pas la fonction f mais la fonction f sur l'objet x ).

Membres statiques A l'opposé de cela, les membres statiques, signalés par la qualification static précédant leur déclaration, sont partagés par tous les objets de la classe. De chacun il n'existe qu'un seul exemplaire par classe, quel que soit le nombre d'objets de la classe. Les données et fonctions membres non statiques sont donc ce que dans d'autres langages orientés objets on appelle variables d'instance et méthodes d'instance, tandis que les données et fonctions statiques sont appelées dans ces langages variables de classe et méthodes de classe. La visibilité et les droits d'accès des membres statiques sont régis par les mêmes règles que les membres ordinaires.

Membres statiques class Point { int x, y; public: static int nombreDePoints; Point(int a, int b) { x = a; y = b; nombreDePoints ++; } };

Membres statiques Chaque objet Point possède ses propres exemplaires des membres x et y mais, quel que soit le nombre de points existants à un moment donné, il existe un seul exemplaire du membre nombreDePoints. Initialisation. La ligne mentionnant nombreDePoints dans la classe Point est une simple annonce , comme une déclaration extern du langage C. Il faut encore créer et initialiser cette donnée membre (ce qui, pour une donnée membre non statique, est fait par le constructeur lors de la création de chaque objet).

Membres statiques Cela se fait par une formule analogue à une définition de variable, écrite dans la portée globale, même s'il s'agit de membres privés : int Point::nombreDePoints = 0; (la ligne ci-dessus doit être écrite dans un fichier .cpp , non dans un fichier .h ) L'accès à un membre statique depuis une fonction membre de la même classe s‘écrit comme l'accµes µa un membre ordinaire (voyez l'accès à nombreDePoints fait dans le constructeur Point). L'accès à un membre statique depuis une fonction non membre peut se faire à travers un objet, n'importe lequel, de la classe : Point a, b, c; cout << a.nombreDePoints << "\n";

Fonctions membres statiques Une fonction membre statique n'est pas attachée à un objet. Par conséquent : elle ne dispose pas du pointeur this de sa classe, elle ne peut référencer que les fonctions et les membres statiques.

static int nombreDePoints; public: static int combien() { Par exemple, voici la classe Point précédente, dans laquelle le membre nombreDePoints a été rendu privé pour en empêcher toute modification intempestive. Il faut donc fournir une fonction pour en consulter la valeur, nous l'avons appelée combien : class Point { int x, y; static int nombreDePoints; public: static int combien() { return nombreDePoints; } Point(int a, int b) { x = a; y = b; nombreDePoints ++; }; Pour afficher le nombre de points existants on devra maintenant écrire une expression comme (a étant de type Point) : cout << a.combien() << "\n";

static int nombreDePoints; public: Point(int a, int b); Point.h Point.cpp class Point { int x, y; static int nombreDePoints; public: Point(int a, int b); static int combien() { return nombreDePoints; } }; #include "Point.H" int Point::nombreDePoints = 0; Point::Point(int a, int b) { //validation de a et b x = a; y = b; nombreDePoints++; }; #include "Point.H" #include <iostream> using namespace std ; void main ( ) { Point p1(2,3); Point p2(2,1); cout<<"val1 "<<p1.combien()<<endl; cout<<"val2 "<<p2.combien()<<endl; }

Fonctions Amies

Fonctions amies La notion de "fonction amie" propose une solution intéressante, sous la forme d'un compromis entre encapsulation formelles des données privées et des données publiques. Lors de la définition d'une classe, il est en effet possible de déclarer qu'une ou plusieurs fonctions (extérieur de la classe) sont des "amies", une telle déclaration d'amitié les autorise alors à accéder aux donnés privées, au même titre que n'importe qu'elle fonction membre.

Fonctions amies Il existe plusieurs situations d'amitiés : Fonction indépendante amie d'une classe Fonction membre d'une classe, amie d'une autre classe Fonction amie de plusieurs classes Toutes les fonctions membre d'une classe amie d'une autre classe.

Fonctions amies Exemple de fonction indépendante amie d'une classe : Soit la fonction coïncide amie de la classe Point (chargée de détecter la coïncidence éventuelle de deux points). class Point { int x,y; public : Point (int abs=0; int ord=0){x=abs; y=ord;} friend int Coincide(Point, Point); };

Fonctions amies int Coincide(Point p, Point q) { if ((p.x==q.x) && (p.y==q.y)) return 1; else return 0; } void main() { Point a(1,0), b(1,2); if (Coincide(a,b)) cout<<"a coincide avec b"<<endl; cout<<"a et b sont différents"<<endl;

Fonctions amies Fonction membre d'une classe, amie d'une autre classe Il suffit de préciser, dans la déclaration d'amitié, la classe à laquelle la fonction concernée, à l'aide de l'opérateur de résolution de portée (::) class A { // partie privée … // partie publique friend int B::f(char, A); }; // f doit pouvoir accéder aux //membres privés de A elle sera //déclaré amie au sein de la classe. class A; class B { … int f(char,A); … }; int B::f(char …, A …) { // on a accès ici aux //membres privés de tout //objet de type A }

Fonctions amies Fonction amie de plusieurs classes : class A { // partie privée … // partie publique friend int B::f(char, A); }; // f doit pouvoir accéder aux //membres privés de A elle sera //déclaré amie au sein de la classe. class A; class B { … int f(char,A); … }; int B::f(char …, A …) { // on a accès ici aux //membres privés de tout //objet de type A }

Fonctions amies Fonction amie de plusieurs classes : class B; class A { // partie privée … // partie publique friend void f(A, B); }; class A; class B { // partie privée … // partie publique friend void f(A, B); };

Surcharge des opérateurs

Opérateurs Mathématiques : +, -, *, /, %, ++, -- Logiques : &&, ||, !, ==, !=, <, >, <=, >= Données : *, &, -> Binaire : &, |, ^, ~, <<, >> Assignation : =, *=, /=, %=, +=, -=, <<=, >>=, &=, |=, ^= Tous surchargeables.

En C++ on peut redéfinir la sémantique des opérateurs du langage, soit pour les étendre à des objets, alors qui n‘étaient initialement définis que sur des types primitifs, soit pour changer l‘éffet d'opérateurs prédéfinis sur des objets. Cela s'appelle surcharger des opérateurs. Il n'est pas possible d'inventer de nouveaux opérateurs ; seuls des opérateurs déjà connus du compilateur peuvent être surchargés. Tous les opérateurs de C++ peuvent être surchargés, sauf les cinq suivants :

Par exemple, la sémantique d'une surcharge de ++ ou <= n'a pas à être liée avec celle de + ou <. Surcharger un opérateur revient à définir une fonction ; tout ce qui a été dit à propos de la surcharge des fonctions s'applique donc à la surcharge des opérateurs. Plus précisément, pour surcharger un opérateur (ce signe représente un opérateur quelconque) il faut définir une fonction nommée opérator .Ce peut être une fonction membre d'une classe ou bien une fonction indépendante. Si elle n'est pas membre d'une classe, alors elle doit avoir au moins un paramètre d'un type classe.

Surcharge d'un opérateur par une fonction membre Si la fonction operator est membre d'une classe, elle doit comporter un paramètre de moins que la pluralité de l'opérateur : le premier opérande sera l'objet à travers lequel la fonction a été appelée. Ainsi, sauf quelques exceptions : « obj » ou « obj » équivalent à obj.operator () «obj1 obj2 » équivaut à obj1.operator (obj2)

int X() const { return x; } int Y() const { return y; } class Point { int x, y; public: Point(int = 0, int = 0); int X() const { return x; } int Y() const { return y; } Point operator+(const Point) const; //surcharge de + par une fonction membre ... }; Point Point::operator+(const Point q) const { return Point(x + q.x, y + q.y); } Point p, q, r; ... r = p + q; // compris comme : r = p.operator+(q);

Exercice 4 redéfinir l’opérateur == correspondant à la fonction coïncide.

Correction4 // Surcharge de l'opérateur == class point { private: int x,y; public: point(int abs,int ord); // déclaration de la fonction amie friend int operator==(point,point); }; #include "Point.h" point::point(int abs,int ord) { x=abs; y=ord;}

#include"Point.h" #include <iostream> using namespace std; int operator ==(point p, point q) { if((p.x==q.x)&&(p.y==q.y)) return 1; else return 0; } void main() point a(4,0),b(4,0),c(0,0); if(a==b)cout<<"a coïncide avec b\n"; else cout<<"a est différent de b\n"; if(a==c)cout<<"a coïncide avec c\n"; else cout<<"a est différent de c\n";

Exercice 5 En utilisant la propriété de surcharge des fonctions du C++, créer - une fonction membre de la classe vecteur de prototype float vecteur::operator*(vecteur); qui retourne le produit scalaire de 2 vecteurs vecteur vecteur::operator*(float); qui retourne le vecteur produit d’un vecteur et d’un réel (donne une signification à v2 = v1 * h;) - une fonction AMIE de la classe vecteur de prototype vecteur operator*(float, vecteur); qui retourne le vecteur produit d’un réel et d’un vecteur (donne une signification à v2 = h * v1;)

On doit donc pouvoir écrire dans le programme: vecteur v1, v2, v3, v4; float h, p; p = v1 * v2; v3 = h * v1; v4 = v1 * h; Remarque: On aurait pu remplacer la fonction membre de prototype vecteur vecteur::operator*(float); par une fonction AMIE de prototype vecteur operator*(vecteur, float);

Correction 5 Vecteur.h class vecteur { private: float x,y; public: vecteur(float,float); void affiche(); // surcharger + vecteur operator+(vecteur); // surcharger * : produit scalaire float operator*(vecteur); // surcharger * : vecteur (passé en paramètre)*scalaire retourné vecteur operator*(float); // surcharger * : vecteur (objet)*scalaire retourné friend vecteur operator*(float,vecteur); };

#include "vecteur.h" #include <iostream> using namespace std; vecteur::vecteur(float abs =0,float ord = 0) {x=abs;y=ord;} void vecteur::affiche() {cout<<"x = "<<x<<" y = "<<y<<"\n";} vecteur vecteur::operator+(vecteur v) {vecteur res(0,0); res.x = v.x + x; res.y = v.y + y; return res;} float vecteur::operator*(vecteur v) {float res = v.x * x + v.y * y; vecteur vecteur::operator*(float f) {vecteur res(0,0); res.x = f*x; res.y = f*y; return res; }

#include "vecteur.h" #include <iostream> using namespace std; vecteur operator*(float f, vecteur v) { v.x = f*v.x; v.y = f*v.y; return v; } void main() vecteur a(2,6),b(4,8),c(0,0),d(0,0); float p,h=2.0; p = a * b; cout<< p <<"\n"; c = h * a; c.affiche(); d = a * h; d.affiche();

CAS PARTICULIERS DES OPÉRATEURS D’INSERTION (<<) ET D’EXTRACTION DE FLUX (>>) Dans le langage C++ nous avons l’opérateur (<<) pour afficher en sortie, et l’opérateur (>>) pour saisir les entrées. Ces opérateurs ont été surchargés, fournis dans une classe de librairie classique (iostream.h) qui vient avec chaque compilateur C++

Normalement dans le langage C, ces opérateurs servent juste pour la décalage à gauche (<<) et le décalage à droite (>>) dans des manipulations bit-à-bit sur des entiers et des chars. Or avec un compilateur C++, on étendu le rôle des ces opérateurs pour remplacer les fonctions fastidieuses d’écriture à l’écran avec printf, et de saisie avec scanf, qui exigent de fournir précisément le format pour chaque type de donnée à afficher ou à saisir.

Malgré cela, même si la surcharge a été effectuée dans la librairie pour reconnaître la plupart des types de données de base comme les entiers (int), les virgule flottantes (float), et les chaînes de caractère (char*), le constructeur de la librairie ne peut pas deviner le type d’objet que vous allez créer plus tard et sur lequel va opérérer l’affichage ou la saisie. Aussi, c’est à vous que revient le rôle d’instruire ces opérateurs du comment procéder avec votre type d’objet.

La classe que vous créez contient alors la déclaration des fonctions d’insertion de flux ostream et d’extraction istream qui deviennent des fonctions amies de la classe: Class nom_de_la_classe { .... friend ostream &operator<<(ostream&, const nom_de_la_classe &); friend istream &operator>>(istream&, nom_de_la_classe &); } Lorsque le compilateur identifiera la séquence: (objet de type ostream) << (objet de type nom_de_la_classe) Il invoquera la fonction définie dans votre classe nom_de_la_classe::operator<<()

Exemple #include <iostream> using namespace std; class Point { double x, y; public: Point(double a, double b); Point operator+( Point rhs) ; friend ostream& operator<<(ostream& os, Point& c); friend istream& operator>>(istream& is, Point& c); };

#include "Point.H" Point::Point(double a, double b) { x=a; y=b; } Point Point::operator+(Point rhs) { return Point(x + rhs.x, y + rhs.y);

#include "Point.H" #include <iostream> using namespace std; ostream& operator<<(ostream& os, Point& c) { os<<"("<<c.x<<" , "<<c.y<<")"; return os; } istream& operator>>(istream& is, Point& c) is>>c.x>>c.y; return is;} void main() Point A(12.347, 15), B(-2, 5.45), Result(0,0); Result=A+B; cout<<Result<<endl;

CAS PARTICULIER DE L’OPÉRATEUR D’ASSIGNATION (=) #include "Point.h" Point:: Point(int a, int b) {x=a; y=b; } Point Point:: operator=(Point p) { this->x=p.x; this->y=p.y; return (*this); #include <iostream> using namespace std; class Point { public : int x ,y; Point(int, int); Point operator=(Point p) ; }; #include "Point.h" #include <iostream> using namespace std; void main() { Point p1(4,7); Point p2(0,0); p2=p1; cout << p2.x<<"et"<<p2.y; }

La classe String

Héritage

Généralités sur l'héritage De nombreuse classes peuvent être conçues comme des spécialisation de classes plus générales (relation EstUn ou SorteDe). L'héritage permet d'éviter de redéfinir dans une classe dérivée le comportement des classes ancêtre, et donc, de ne se préoccuper que de comportement particulier à la classe. On organise les classes selon une hiérarchie (arbre d'héritage ou graphe si héritage multiple).

Généralités sur l'héritage (suite) Sémantique de l'héritage si on a A …  B (B hérite de A) alors B doit pouvoir s'employer partout où A est employé : principe de SUBSTITUTION Sous typage Les descendants d'une classe en sont des sous-types Si on a ABC …, tout objet instance d'une classe héritant de A (donc de classe A,B,C, …) est de "type A". On peut lui appliquer toutes les opérations définies dans la classe A. Noter qu'un objet peut être de "type A" sans être une instance d'une classe héritant de A.

Exemple d'arbre d'héritage Séquence Pile Collection Nom séquentielle Dictionnaire Ensemble

Arbre d'héritage (autre exemple) Ligne Forme Rectangle Cercle Ellipse Carré Principe de substitution : toute opération applicable à une instance de Forme doit être applicable à une instance de Ligne, Ellipse, Cercle, …

L'héritage en C++ class classeDérivée : [mode] classeDeBase { //complément d'interface pour "classeDérivée" } ; mode = public  cas le plus fréquent = private  pour héritage partiel(par défaut) = protected  les membre publics seront considérés comme protégés Héritage multiple : class A : public B, public C {… … …};

Exemple d'héritage class Pixel:public Point //un pixel est un point // définition de la classe pixel par dérivation de la classe point class Pixel:public Point //un pixel est un point // ou une sorte de point {public : enum Couleur {NOIR,ROUGE,BLANC}; Pixel(Couleur laCouleur, int abs, int ord); //la construction d'un pixel s'appuie sur la construction d'un Point void Colorer (Couleur laCouleur = NOIR) { coul=laCouleur;} protected : Couleur coul; };

Appels des Constructeurs /destructeurs Pour un objet de classe C A la création Classe A Classe B Classe C Appel de A() Puis de B() Puis de C() Appel de ~C() Puis de ~B() Puis de ~A() A la destruction Héritage Simple

Appels des Constructeurs /destructeurs ClasseC : public classeA, public classeB ClasseA Classe B Classe C A la création Appel de A() Puis de B() Puis de C() A la destruction Appel de ~C() Puis de ~B() Puis de ~A() Héritage Multiple

Liaison entre constructeurs Si le constructeur de la superclasse à des arguments, on doit préciser, avant le corps des constructeurs des classes dérivées (dans l'interface si "inline", sinon dans la réalisation de la classe), quels arguments lui transmettre, avec une syntaxe similaire à l'initialisation des attributs Exemple : class Pixel : public Point { public : // … Pixel (Couleur lacouleur, int abs=0, int ord =0) :Point(abs,ord), coul(lacouleur){} //… //appel du constructeur // initialisation de l'attribut //de la super-class // de la classe dérivée };

Exemples d'héritage #include "Point.h" class Forme { public : void Deplacer (Point vecteur); void Dessiner () const; Forme (Point orig) : origine(orig) {}; protected : Point origine; };

Exemples d'héritage { public : void Dessiner() const; // redéfinition class Rectangle : public Forme //un rectangle est une Forme { public : void Dessiner() const; // redéfinition Rectangle (Point basGauche, long longueur, long hauteur) : Forme(basGauche), diagonale(longeur, hauteur) {}; protected : Point diagonale; //attribut supplémentaire }; class Carre:public Rectangle //un carré est un rectangle {public : Carre(Point orig, long cote) : Rectangle(orig,cote,cote){}; }; //pas d'attribut ni d'autre méthode spécifié à carre

Visibilité des éléments hérités Le mode d'héritage conditionne la visibilité, dans la classe dérivée, des éléments (attributs et méthodes) hérités de la superclasse. Héritage public : accès aux "public" et aux "protected" Héritage private : tout les éléments deviennent "private", mais on peut indiquer explicitement les éléments "public" que l'on souhaite laisser "public", donc accessibles aux clients de la classe dérivée. Héritage protected : les éléments "public" deviennent "protected", les autres sont inchangés

Affectations d'objets Point (int abs, int ord); /* supposons que la classe Point soit munie d'un constructeur Point (int abs, int ord); et que la classe Pixel qui hérite de Point ait un constructeur Pixel (Couleur laCouleur, int abs, int ord); */ Point p(1,2); Pixel px( Pixel :: VERT, 3,5); p=px; //autorisé, affectation partielle // p demeure un Point; p est en (3,5) // Noter que le type couleur étant défini dans l'espace de // noms de Pixel, on doit préciser cette appartenance dans // l'écriture des valeurs de type Couleur, comme ici // pixel : :VERT

Affectations de pointeurs d'objets Point p(1,2), *pp; Pixel *ppx; Pixel px(Pixel::VERT,3,5); pp=&px; //autorisé, car on peut affecter à un pointeur //de Point l'adresse d'un objet d'une classe //descendante, mais ppColorer(…); //est interdite ppx=&p; //de même ceci est interdit !

Spécialisation par redéfinition de méthodes class Point { public : virtual void Afficher(); {cout <<x<<","<<y<<endl;} protected : int x,y; //x et y sont visibles des classes descendantes }; class Pixel:public Point { public : virtual void Afficher() //redéfinition de afficher {cout<<x<<""<<y<<"couleur:"<<coul<<endl;} //on aurait pu réutiliser la méthode de l'ancêtre, avec le corps suivant // { cout<<"couleur:"<<coul<<""; // Point::Afficher();}

Liaison statique/dynamique Point * pt = new Point (2,6); Pixel px(Pixel::VERT, 3,5); ptAfficher(); // appel de la méthode Point::Afficher() pt=&px; ptAfficher(); //appel de la méthode pixel::Afficher() C'est pour obtenir ce fonctionnement (liaison dynamique), que l'on a placé l'attribut "virtual" devant la déclaration de la méthode dans l'interface de la classe de base : Virtual typeRetour NomMethode (listeParamétres); //NomMethode ne peut pas être redéfinie dans les descendant // qu'avec la même liste de paramètres et le même type de retour Si l'on ne précise pas l'attribut "virtuel" dans la classe de base, on aurait eu une liaison statique : appel de la méthode définie dans la classe de déclaration du pointeur.

Danger de la liaison statique class Point {public : Point (int,int); void Afficher(); void Deplacer(int dx, int dy); {x+=dx; y+=dy; Afficher();} protected : int x,y; }; class Pixel:public Point {public : Pixel (Couleur,int,int); void Colorer(Couleur); protected : Couleur c; Point p(1,5); Pixel px(ROUGE, 4,8); p.Afficher(); // Affiche 1,5 Px.Afficher(); // Affiche 4,8 couleur:rouge p.Deplacer(1,1); // affiche 2,6 Px.Deplacer(1,1); // affiche 5,9 // car c'est la méthode Point::Afficher() qui à été appelée !!! // remède : virtual void Afficher() dans Point

Classe abstraites Le seul but de certaines classes est de servir uniquement de classes de base, et non d'être instanciables : on parle de classes abstraites (cas de Forme) En c++, une classe est considérée comme abstraite, si sa définition contient une méthode virtuelle pure ou son constructeur est déclaré protected. Exemple pour Forme, virtual void Dessiner()=0; Le compilateur c++ interdit d'instancier la classe Forme et vérifie que tous ces descendants redéfinissent la méthode virtual void Dessiner();

Forme canonique d'une classe class T { public : // … autres méthodes T(); T & operator =(const T &); // affectation T(const T &); // constructeur de copie virtual ~T(); // destructeur private : … protected : … }; /* des lors qu'une fonction dispose de pointeurs sur des parties dynamiques, la copie d'objet de classe (aussi bien par le constructeur de recopie par défaut que par l'opérateur d'affectation) n'est pas satisfaisante. */

Création d'une classe simple par héritage d'un exemplaire de classe générique //on peut combiner héritage et généricité. Exemple : Template<class T> //Point est une classe générique class Point { public : Point(T abs=O, T ord=0):x(abs), y(ord){} void Afficher(); protected : T x,y; }; class Pixel:public Point<int> //pixel hérite de l'exemplaire // Point<int> {public: Pixel(Couleur coul, int abs=0, int ord=0) :c(coul), Point<int>(abs,ord){} protected : Couleur c;

Création d'une classe générique par héritage //on peut obtenir une classe générique par héritage d'une classe //Exemple : template<class T> //héritage d'une classe simple B class A:public B { //… }; template<class T> //héritage d'une classe générique class Vecteur:public Vect<T> {//…

Classe C++ : interface /** * A calendar date. */ class Date { public : void set_date(int year,int month,int day ); void print() const; private : int year_; int month_; int day_; }; file.h

Classe C++ : implantation file.cpp void Date::set_date(int year, int month,int day) { if (month == 2 && ! bisextile(year) && day > 28 ) { cerr << “Where did you see that\n” << “February could have “ << day << “ days “ << “in “ << year << “!” << endl; }

Constructeurs / Destructeurs class Date { public : /** * Creates a Date from the year, month and day. * if parameters are not consistent, an error * message is produced. */ Date( int year, int month, int day ); /** Copy constructor */ Date( const Date& other_date ); /** Destroys this Date */ ~Date(); // … }; file.h