0% ont trouvé ce document utile (0 vote)
5 vues132 pages

Introduction à la programmation C++

Transféré par

malek.gritli.edu
Copyright
© All Rights Reserved
Nous prenons très au sérieux les droits relatifs au contenu. Si vous pensez qu’il s’agit de votre contenu, signalez une atteinte au droit d’auteur ici.
Formats disponibles
Téléchargez aux formats PDF, TXT ou lisez en ligne sur Scribd
0% ont trouvé ce document utile (0 vote)
5 vues132 pages

Introduction à la programmation C++

Transféré par

malek.gritli.edu
Copyright
© All Rights Reserved
Nous prenons très au sérieux les droits relatifs au contenu. Si vous pensez qu’il s’agit de votre contenu, signalez une atteinte au droit d’auteur ici.
Formats disponibles
Téléchargez aux formats PDF, TXT ou lisez en ligne sur Scribd

La programmation Orientée Objet

le langage C++
Cours : Gritli Malek

Date : Septembre 2014

Version : V 1.0

Gritli malek 1
Bibliographie

● [LIPPMAN98] Lippman, Stanley, and Josée Lajoie, C++ Primer,


3rd Edition, Addison WesleyLongman, Inc.,Reading, MA 1998)
ISBN 0-201-82470-1.

● [LIPPMAN02]Essential C++ ,By Stanley B. Lippman ,Publisher :


Addison Wesley

Gritli malek 2
Pourquoi le langage C++ ?
● C++ est un ''C amélioré''

Le langage C Les apports de C++

Fonctions scanf et printf Les flots d’entrée-sortie (cin/cout)


Les chaînes de caractères Les objets de la classe string
Le passage d’argument ''par adresse '' La notion de référence qui augmente de la
performance du passage paramètres des
fonctions
C autorise la définition des variables Le C++ autorise la définition de classes
structurée simple (attributs, fonctions membres,visibilité des
membres), et la mise en œuvre des relations
(notamment de composition ou
despécialisation) entre les classes.

La programmation procédurale (POO) Un objet est une variable structurée


avec différents attributs + Encapsulation des
données

Gritli malek 3
Les notions du C utilisées en C++
● Le C++ est une surcouche au langage C qui intègre les concepts de la POO
● L’objectif de B. Stroustrup en créant le C++ était de faciliter aux programmeurs C la
transition vers la programmation orientée objet.
● Le C++ est un langage typé. Le compilateur C++ vérifie donc la compatibilité des
types lors des opérations (affectations et des passages de paramètres aux
fonctions...)
● Types de base : (char, short, int, long, float,double), et les variables non signées
(unsigned char, unsigned short, unsigned int)
● Le nombre d’octets assigné à chacun de ces types peut varier d’un compilateur à un
autre.

int var;
printf("taille de var=%d, taille d’un char= %d",sizeof(var),sizeof(char));

Gritli malek 4
Les pointeurs

● Une variable de type T* est destinée à recevoir l’adresse d’une variable de type T .
● Lorsque ptr est une variable pointeur de type T* , l’opérateur * permet de
dé-référencer ce pointeur : *ptr est la variable de type T dont l’adresse est stockée
dans la variable ptr.
● & permet de fournir l’adresse d’une variable

int var=12; // variable de type int déclarée et initialisée


int * ptr; // variable de type pointeur sur int non initialisée.
*ptr=45 ici, la variable pointeur n’est pas initialisée !!!
ptr=&var ; // ptr initialisée avec l’adresse de la variable var.
*ptr=45 ; // *ptr dé-référence ptr. A cet instant, *ptr est la variable var.

Gritli malek 5
Les pointeurs

● Les pointeurs sont typés, le compilateur réalise aussi des vérifications de type impliquant
les pointeurs
int varI=12;
int * ptrI=&varI;
char varC=12;
char* ptrC=&varC;
ptrC=&varI ; // erreur de compilation : char * non compatible avec int *
ptrI=&varC ; // idem : int * pas compatible avec char *
● Le nom d’un tableau correspond à l’adresse de sa première case, une variable pointeur
représente l’adresse de la première case d’un tableau

int tab[3]={1,2,7}; //tableau de 3 int initialisés


int * ptrI=tab ; //icite, identique à ptrI=&tab[0]
PtrI[2]=3 ; //licite : ptrI[2] équivaut ici à tab[2]
ptrI=&tab[1] ; //licite aussi , identique à ptrI=tab+1
ptrI[1]=2 ; //ptrI[1] équivaut maintenant à tab[2]

● Rappelons que les chaînes de caractères en C sont aussi des tableaux

Gritli malek 6
Les structures de contrôle
● Le C++ utilise la même syntaxe que le langage C pour les différentes structures de
contrôle. Les structures de contrôle sont if/else, for et while (ou do ... while).
● La structure if(...){...}else{...}

if((entier%2)==0)
{
printf("Entier pair");
}
else
{
printf("Entier impair");
}

Gritli malek 7
Les structures de contrôle
● La structure while(...){...} ou do{...}while(...);

do
{
printf("Saisir un entier impair :\n");
scanf("%d",&entier);
}
while((entier%2)==0); //boucle tant que entier pair

● La structure for(...;... ;...){...}

for(indice=1;indice<10;indice++)
{
tab[indice]=tab[indice-1]+indice;
}

Gritli malek 8
Les conversions de type (cast)

● Le compilateur C++ utilise et vérifie les types (opérations réalisées cohérentes, les
paramètres passés à une fonction correctes …)
● La conversion implicite de types (conversions que le compilateur peut déduire grâce au
contexte) : certaines conversions implicites génèrent tout de même des mises en garde
(warnings) (lorsque l’on compare un entier signé avec un entier non signé), aussi
quand on affecte la valeur d’une variable de type float à une variable de type int (ou
l’inverse) (Cette conversion est dégradante (perte d’information) dans le sens float->
int)
● Quelques conversions de types :
int varI=513; // variable entière sur 32 bits signé
char varC; // variable entière sur 8 bits signé
float varF=2.5 ; // variable utilisant la norme flottant IEEE 32 bits
varC=varI; // conversion dégradante
varI=varF; // ici, un warning signale « possible loss of data »
printf("%d %d",varC,varI); // affiche 1 (513mod256) et 2(partie entière de 2.5)

Gritli malek 9
Les conversions de type (cast)

● Les incompatibilités de type conduisent à l’échec de la compilation, pour l'éviter il faut


utiliser la conversion explicite

int varI=513;
char * ptrC=(char *) &varI; // conversion int*-> char* demandée
printf("%d %d",ptrC[0],ptrC[1]); // affiche les deux octets de poids faible de varI

● L’abus de conversions explicites, notamment pour régler des warnings lors de la


compilation, peut conduire à de grosses erreurs de programmation.

int varI=513;
char * ptrC=(char *) &varI; //cast int * -> char *
int adresse=(int) ptrC; // cast char * -> int (ça c’est vraiment douteux
int * pI=(int *) adresse; // cast int -> int * (aussi douteux)

Gritli malek 10
Les conversions de type (cast)
● Quelques cast utiles :

➢ T * -> V* : conversion de pointeurs. Les adresses restent intactes car elles ont la
même taille. Mais ces adresses ne désignent pas des variables de même type ! A
utiliser avec précaution.

➢ float -> int : (dégradant) conserve la partie entière.

➢ int -> float : passage au format flottant IEEE

➢ char -> int : la variable passe sans altération de 8 bits à 32 bits

➢ int -> char : dégradant : les 32 bits sont tronqués aux 8 bits de poids faible.

Gritli malek 11
Passage par valeur
● Les paramètres sont passés à une fonction par copie , c’est-à-dire via la pile
● Le programme qui utilise une fonction recopie dans la pile les paramètres effectifs de
la fonction, la fonction appelée n’a accès (dans la pile) qu’à une copie des
paramètres effectifs, et non à l’original
● Les paramètres effectifs et les variables locales sont des variables temporaires qui
n’existent que le temps d’exécution de la fonction

void MaFonction(int param) // param ppv pAppel


{
param++ ;
// ici, param est modifié mais pas pAppel
}

int main()
{
int pAppel=23; // paramètre d'appel
MaFonction(pAppel); // pAppel est recopié dans param
printf("d",pAppel); // pAppel vaut toujours 23
}

Gritli malek 12
Passage par adresse

● Lorsque l’on souhaite qu’une fonction modifie une variable, on doit lui fournir l’adresse
de la variable à modifier

void MaFonction(int * adrVariable)


{
(*adrVariable)++;
}
void main()
{
int pAppel=23;
int * adrAppel=&pAppel;
MaFonction(adrAppel) ;
printf("pAppel =%d",pAppel); //pAppel vaut ici 24
}

Gritli malek 13
Transmission des paramètres : Performances

● Le mode de transmission agit sur la rapidité d’exécution.


● Une variable de type personne contient 3 champs : deux tableaux de 30 caractères
et un entier non signé (taille de 64 octets)

struct personne
{
char nom[30];
char prenom[30];
unsigned age;
};

● Pour les pointeurs sur des variables structurées, la notation ptr->nom est équivalente
à(*ptr).nom

Gritli malek 14
Transmission des paramètres : Performances

● La fonction AfficheBis(personne *) est plus rapide que la fonction Affiche(personne)


pour un service équivalent. Pourquoi ?
● L’appel de la fonction Affiche(personne) nécessite une recopie d’une variable de 64
octets dans la pile, alors que l’appel de la fonction AfficheBis(personne *) ne requiert
que la copie d’un pointeur de 32 bits (4 octets).
void Affiche(personne p)
{
printf(" nom : %s prenom : %s age : %d \n",[Link],[Link],[Link]);
}
void AfficheBis(personne * ptr)
{
printf(" nom : %s prenom : %s age : %d \n",ptr->nom,ptr->prenom,ptr->age);
}
int main( )
{personne AM={"Martin","Arthur",32};
printf("sizeof(AM) = %d\n",sizeof(AM)); // affiche : sizeof(AM)=64
Affiche(AM);
AfficheBis(&AM); // exécution plus rapide que Affiche(AM)}
Gritli malek 15
Les paramètres constants

● L'utilisation du modificateur const pour les paramètres d’entrée (paramètres qui ne sont
pas censés évoluer dans la fonction).
● Le compilateur indique qu’une variable (ou un objet) constante est à gauche d’une
affectation (l-value)

float Division(const int num,const int den)


{
if(den=0) return num; // error C2166: l-value specifies const object
else return (float)num/den ;
}

Gritli malek 16
Les références en C++

T var;
T & ref = var; // ref est une référence à var
● Ci-dessus, ref est une référence à la variable var de type T
● Au contraire des pointeurs, les identificateurs var et ref désignent la même variable et
manipuler l'un, revient à manipuler l'autre
● L'initialisation d'une référence lors de sa déclaration est obligatoire

#include <iostream>
int main( )
{int I=3;
int &refI = I;
printf("I = %d \n",I);
printf("refI = %d \n",refI);
printf("adresse de I = %x \n",&I);
printf("adresse de refI = %x \n",&refI);
printf("Incrément de I (I++)");
I++;
printf("refI = %d \n",refI);}
Gritli malek 17
Transmission de paramètres par références
● L’utilisation de références en C++ concerne l’échange d’informations avec une fonction

#include <stdio.h>
void f(int &); // noter que le pramètre est une référence
Int main( )
{
int pAppel=5;
f(pAppel);
printf("pAppel = %d \n",pAppel); // Résultats pAppel = 6
}
void f(int & pFormel) // pFormel est un alias de pAppel
{Pformel++; // ici on modifie réellement le paramètre d’appel, grâce à l’alias}

● Ici le paramètre formel pFormel de la fonction f(int & ) est une référence au paramètre
d’[Link] ce cas, manipuler le paramètre formel pFormel revient à manipuler le paramètre
d’appel. Par la suite,on appellera ce mode de transmission passage par référence → convient
aux paramètres de sortie et aux paramètres d’entrée/sortie
● Lors de l’appel de la fonction, seule l’adresse du paramètre passé par référence est transmise
à la fonction
Gritli malek 18
Les paramètres passés par référence
constante : efficacité

● Avantages : Le passage par référence évite ainsi certaines duplications d’objets


volumineux (nécessitant beaucoup d’octets en mémoire) → gain en mémoire
● Inconvinients : cela expose aussi le programmeur à des erreurs de programmation qui
se répercutent sur le programme appelant (modifier le paramétre d 'appel ). pour éviter
cette modification il faut introduire le modificateur const dans les paramétres
● Un paramètre de sortie est une donnée fournie par le code appelé au code appelant, il
n'est pas possible de définir de paramètres de sortie autrement qu'en utilisant un
paramètre d'entrée par son adresse (le langage C)

void PassageParReferenceConst(const personne & p)


{
/* mode de transmission privilégié en C++ pour les paramètres d’entrée */
printf(" nom : %s prenom : %s age : %d \n",[Link],[Link],[Link]);
}

Gritli malek 19
Bilan sur le passage de paramètres en C++
● Paramètre d’entrée d’une fonction : paramètre dont la valeur est utilisée par la fonction
pour réaliser un traitement mais dont la valeur n’est pas modifiée par la fonction
● Paramètre de sortie un paramètre dont la valeur en entrant dans la fonction est sans
importance mais dont la valeur va être modifiée par la fonction
● Paramètres d’entrée/ sortie

Passage par valeur


Pas volumineux

Paramètre d’entrée
Volumineux Passage par référence constante

Les paramètres de sortie et d’entrée/sortie Passage par référence

Gritli malek 20
Bilan sur le passage de paramètres en C++ :
exemples

● Incrementer(E/S param : entier)

void Incrementer(int & param)


{
Param++ ;
}
● PlusUn(E param :entier)

int PlusUn(int param)


{
return (param+1);
}
● Random(S r:entier)
void Random(int & r)
{
r=rand();
}
Gritli malek 21
Valeurs par défaut des paramétres
● En C++, des valeurs par défaut peuvent être fournies à certains paramètres

#include <stdio.h>
float Addition(float, float =3); //2nd paramètre avec valeur par défaut figure dans le
prototype
int main( )
{
printf("%f \n",Addition(10,5)); // 15
printf("%f \n",Addition(7)); //10
}
float Addition(float a, float b)
{return a+b;}

● Les arguments ayant des valeurs par défaut doivent tous figurer en fin de liste
desarguments

Float Addition(float=2, float =3); //ok


Float Addition(float=2, float); // Non
Float Addition(float=1, float, float =3); // Non plus
Float Addition(float, float=5, float =3); // ok
Gritli malek 22
La surcharge de fonctions

● Signature d'une fonction : la combinaison de sa classe (si elle est membre d’une
classe) de son identificateur et de la suite des types de ses paramètres. Les 3
fonctions suivantes ont des signatures différentes :

float Addition(float);
float Addition(int,float);
float Addition(float,float );

● Le type de la valeur de retour n’appartient pas à la signature.

● En C++, il est possible de définir plusieurs fonctions avec le même identificateur mais
de signature différentes = la surcharge de fonction

Gritli malek 23
Introduction à la POO
●Dans chacune des classes, les objets sont caractérisés par des attributs et des
comportements

La valeur des attributs à un instant donné caractérise l’état d’un objet


Le comportement d’un objet peut avoir des conséquences sur son état et inversement

●Une classe d’objets va décrire quels sont les attributs et les comportements retenus pour
décrire un objet au sein d’un programme

Gritli malek 24
Notion d'encapsulation des données

● Un objet contient des données et des méthodes prévues pour accéder à ces données

● La POO autorise la dialogue avec un objet qu'à travers l'interface constituée par
l'ensemble de ses méthodes

● Les méthodes jouent donc le rôle d'interface entre les données et l'utilisateur de l'objet.
Elles filtrent les accès aux données

● Pour assurer cette encapsulation, des mot-clés définissent la visibilité des membres
d'une classe.

Gritli malek 25
Les relations entre les objets
(par extension, entre les classes)
● Différentes relations peuvent apparaître entre des objets ou entre des classes.
● Deux objets peuvent être en relation du type composant /composite (UML introduit un
lien avec un losange du côté du composite)

● On peut décrire une classecomme


représentant un sous-ensemble
d'une classe plus générale. La
relation entre une classe et une
sous-classe est une relation de type
spécialisation/généralisation

Gritli malek 26
Les classes en C++

● Certain classe standard fournie par C++ : les classes d’entrée-sortie


● L'objet cin est une instance de la classe istream (entrée standard)
● L'objet cout est une instance de la classe ostream (entrée standard)
● Les classes istream et ostream sont définies dans le fichier d’entête iostream
#include<iostream> // définition des classes ostream et istream
using namespace std; // espace de nom utilisé pour ces classes (explication
ultérieure)
int main()
{
char chaine[10]="Toto";
int age=12;
cout << chaine << " a " << age << " ans.";
cout << "Saisir un entier : \n";
cin >> age;
cout << "Valeur saisie = " << age;
}
Gritli malek 27
Définition d'une classe

● Le mot clé class pour définir une classe d’objets avec des champs de données typés
(attributs) et des fonctions membres (méthodes)
● La classe ci-après est une classe de points à coordonnées entières, La définition de
la classe est située dans un fichier d’entête (point.h )

// point.h fichier d’entête contenant la définition de la classe


#include<iostream>
#ifndef __POINT__
#define __POINT__
class Point
{
public: //partie publique de la classe
void SetX(const unsigned x); // les méthodes
void SetY(const unsigned y);
void SetXY(const unsigned x,const unsigned y);

Gritli malek 28
Définition d'une classe

unsigned GetX() const;


unsigned GetY() const;
float Distance(const Point & p) const;
void Translate(const unsigned x,const unsigned y);
void Display(std::ostream & flot) const;
private: // partie privée (encapsulée)
unsigned _x; // les attributs
unsigned _y;
}; // <- ne pas oublier ce point virgule
#endif

Gritli malek 29
L'implémentation d'une classe
● Le fichier d’implémentation des méthodes (ici [Link] ) contient le code des
méthodes de la classe.

#include "point.h" //inclusion du fichier d’entête de la classe


#include<math>
void Point::SetX(const unsigned int x)
{
_x=x;
}
void Point::SetY(const unsigned int y)
{
_y=y;
}
void Point::SetXY(const unsigned int x,const unsigned int y)
{
_x=x;
_y=y;
}
unsigned Point::GetX() const{
return _x;
}
Gritli malek 30
L'implémentation d'une classe

unsigned Point::GetY() const{


return _y;
}
void Point::Translate(const unsigned Dx, const unsigned Dy)
{
_x=_x+Dx;
_y=_y+Dy;
}
float Point::Distance(const Point & p) const
{
return sqrt((_x-p._x)*(_x-p._x)+(_y-p._y)*(_y-p._y));
}
void Point::Display(std::ostream & flot) const
{
// affichage sur flot de sortie
flot << "(" << _x << "," << _y << ")";
}

Gritli malek 31
Utilisation de la classe

● Une simple utilisation de la classe d'objets Point

// [Link] : fichier d’utilisation de la classe point


#include "Point.h" // inclusion de la définition de la classe Point
using namespace std;
int main()
{
Point p1,p2; // p1 et p2 sont deux objets de la classe Point
[Link](10,10); // donne l’état (10,10) à l’objet p1
[Link](20,20); // donne l’état (20,20) à l’objet p2
[Link](cout); // affiche p1 sur le flot de sortie
[Link](cout); // affiche p2 sur le flot de sortie
cout << "Distance p1 - p2 :" ;
cout << [Link](p2) << endl; //affiche la distance p1-p2
// p1._x=12 ; est interdit par le compilateur, les données sont privées
}

Gritli malek 32
A retenir sur les classes

● Les champs privés ne sont pas accessibles par l’utilisateur de la classe

● Les attributs privés sont néanmoins accessibles aux objets de la classe, c’est-à-dire
dans la définition des méthodes (regarder le code de la méthode distance)

● Deux objets de la même classe ont les mêmes attributs mais pas nécessairement le
même état

● L’état d’un objet de la classe Point est indéfini avant l’utilisation de la méthode
Point::SetXY()
● Faut il réaliser une initialisation correcte des attributs dès la création d’un objet ?
(oui ) → donc on doit ajouter des méthodes appelées constructeurs.

Gritli malek 33
A retenir sur les classes

● Le rôle d’un constructeur est de donner une valeur initiale (état initial) aux attributs.

● Un constructeur est une fonction membre qui porte comme nom le nom de la classe
et qui ne retourne rien

● Un constructeur peut avoir zéro ou plusieurs arguments, éventuellement avec valeurs


par défaut

● Il peut y avoir plusieurs constructeurs dans la même classe (surcharge) dans la


mesure où ils ont des signatures différentes

Gritli malek 34
Les constructeurs
● De multiples constructeurs au sein d'un classe d'objets Point

// point.h : définition de la classe


#ifndef __POINT__
#define __POINT__
#include<iostream>
class Point
{
public:
// les constructeurs
Point();
Point(const unsigned x, const unsigned y);
void SetXY(const unsigned x,const unsigned y);
... le reste est identique ...
private:
unsigned _x;
unsigned _y;
};
#endif

Gritli malek 35
Les constructeurs

● L'implémentation des constructeurs

// [Link] : définition des fonctions membres


#include "Point.h"
#include<math.h>
Point::Point() // constructeur sans argument
{
_x=0;
_y=0;
}

Point::Point(const unsigned x, const unsigned y) // constructeur à 2 arguments


{
SetXY(x,y); // pourquoi pas
}

... le reste est identique ...

Gritli malek 36
Les constructeurs

● la syntaxe d’utilisation des constructeurs dans le fichier ([Link]).

// utilisation de la classe Point munie de constructeurs


#include "Point.h"
using namespace std;
void main()
{
// utilisation du constructeur Point::Point(const unsigned,const unsigned)
Point p1(10,20); // p1 est initialisé à l’aide du constructeur à 2 arguments
// l’état de p1 est (10,20)
// utilisation du constructeur Point::Point()
Point p2; // p2 est initialisé avec le constructeur sans argument
// l’état de p2 est (0,0)

[Link](cout); // affiche (10,20)


[Link](cout); // affiche (0,0)

Gritli malek 37
Sélecteurs/accesseurs et modificateurs

● Pour la classe Point ,les modificateurs sont SetX(), SetY(), SetXY(), les sélecteurs sont
GetX(), GetY() ,les accesseurs sont Display(), Distance().

Qui modifient l’état de


l’objet Modificateurs
Méthodes d’une classe
Qui ne modifient pas l’état de l’objet

Celles
Celles qui utilisent l’état en lecture
qui retournent l’état de l’objet

Sélecteurs Accesseurs

Gritli malek 38
Sélecteurs/accesseurs et modificateurs
● Nous devons suivi les sélecteurs/accesseurs par un mot clé : const

class Point
{
public:
// modificateurs
void SetX(const unsigned x);
void SetY(const unsigned y);
void SetXY(const unsigned x,const unsigned y);
void Translate(const unsigned x,const unsigned y);
// sélecteurs
unsigned GetX() const;
unsigned GetY() const;
// accesseurs
float Distance(const Point & p) const;
void Display(std::ostream & flot) const;
};

Gritli malek 39
Constructeur de copie, opérateur
d’affectation

Un constructeur de copie sert à créer un nouvel objet ayant le même état qu’un objet
de la même classe.

// point.h : définition de la classe


#ifndef __POINT__
#define __POINT__
#include<iostream>
class Point
{
public:
Point(const Point & p); // prototype du constructeur de copie
Point & operator=(const Point & p); // prototype de l’opérateur =
...
};
#endif

Gritli malek 40
Constructeur de copie, opérateur
d’affectation

● L'implémentation du constructeur de copie, et programmer le comportement de l’opérateur =


portant sur des objets d’une classe.
● L’opérateur d’affectation est donc simplement une méthode de la classe Point .
// [Link] :
#include "Point.h"
Point::Point(const Point & p)
{
// recopie de l’état de p dans l’objet courant
_x=p._x;
_y=p._y;
}
Point & Point::operator=(const Point & p)
{
// operateur = pour l’affectation de l’état d’un Point
_x=p._x;
_y=p._y;
return (*this); // retourne l’objet courant.
}
...
Gritli malek 41
Utilisation du constructeur de copie,
opérateur d’affectation

// mise en évidence de l’utilisation du constructeur de copie et de l’opérateur =


#include "Point.h"
using namespace std;
void main()
{
Point p1(10,20);
Point p3;
Point p2(p1); // p2 est créé par copie de p1
// l’état de p2 est (10,20)

p3=p1; // équivaut à [Link]=(p1);

[Link](cout) ; // affiche (10,20)


}

Gritli malek 42
Le pointeur this

● Dans la définition de l’opérateur =, on voit l’utilisation d’un membre appelé this


● Tout objet peut connaître sa propre adresse, this contient l’adresse de l’objet courant

Point & Point::operator=(const Point & p)


{
_x=p._x;
_y=p._y;
return (*this); // retourne l’objet courant.
}
● Ci-dessous, p2=p1 équivaut à [Link]=(p1) et cette méthode retourne p2

int main(){
Point p1(10,20), p2,p3;
p3=p2=p1;
/* équivaut à [Link]=([Link]=(p1)); */
}
● Il est possible de surcharger la plupart des opérateurs
● Il n’est pas possible de redéfinir les opérateurs agissant sur les types élémentaires tels
que int , float , etc.
Gritli malek 43
Les opérateurs en C++
● L’opérateur sera qualifié d’opérateur unaire (1 seul opérande) ou d’opérateur binaire (2
opérandes). l’opérateur « = » est un opérateur binaire, la syntaxe d’utilisation de cet
opérateur étant Op1 = Op2 . L’ opérateur « ++ » en revanche est un opérateur unaire.
En effet, il s’applique à un seul opérande : Op1++ .

● Il y a en fait deux opérateurs « ++ ». Celui qui pré-incrémente et qui s’utilise ++Op1 et


celui qui post-incrémente et qui s’utilise Op1++ . Idem pour les opérateurs de
décrément «--» .

● Certains opérateurs peuvent avoir une version unaire et une version binaire. C’est le
cas par exemple de l’opérateur « - ». La syntaxe a-b utilise l’opérateur – binaire
(soustraction) tandis que –a utilise l’opérateur unaire -(opposé)

● Certain opérateurs ont un traitement par défaut (affectation sur des variables
structurés,l’opérateur & (opérateur unaire) retourne l’adresse de l’objet auquel il
s’applique.

Gritli malek 44
Surcharger des opérateurs en C++

● Les différents opérateurs que l’on peut surcharger en C++ sont les suivants :

Unaire + - ++ – ! ~ * & new new[] delete (cast)

Binaire * / % + - << >> < > <= >= == !=

Binaire & | && ||

Binaire += -= *= /= %= &= ^= |= <<= >>= ,

Gritli malek 45
Surcharger des opérateurs en C++

● La définition des opérateurs passe simplement par l’écriture de fonctions ayant un nom
particulier comportant le mot clé operator suivi du signe de l’opérateur
● Le programmeur aura le choix entre la possibilité de définir ces opérateurs comme
fonctions membres d’une classe ou comme fonctions non membres
● Certains opérateurs ne peuvent être définis que s’ils appartiennent à une classe
comme les suivants :

= (opérateur d'affectation)
( ) (opérateur fonction)
[ ] (opérateur d’indexation)
->

Gritli malek 46
Surcharger des opérateurs en C++

● Un opérateur est utilisé sur un type défini par le programmeur (une classe ou un type
structuré)
● L’emploi de cet opérateur est équivalent à l’appel d’une fonction particulière qui peut
être hors classe ou membre d’une classe (ou d'une structure)
● On définit une nouvelle classe CLS , et deux objets x et y de la classe CLS , alors la
syntaxe (1) peut être équivalente à (2) ou (3) pour le compilateur C++

(1) x+y;
(2) operator+(x,y); // opérateur non membre de la classe CLS
(3) [Link]+(y); // opérateur membre de la classe CLS

Gritli malek 47
Exemple : classe de nombres rationnels

● On fournit ici une ébauche d’une classe de nombre rationnels mettant en évidence l'intérêt
des opérateurs.

rationnel.h : fichier d’entête de la classe Rationnel


#ifndef __RATIONNEL__ // prototypes des opérateurs hors classe
#define __RATIONNEL__ std::ostream & operator<<(std::ostream &
#include<iostream> flot, const Rationnel & r);
class Rationnel
{ Rationnel operator*(const Rationnel & r1,
public: const Rationnel & r2);
Rationnel(int num=0, int den=1); #endif
Rationnel(const Rationnel & r);
int GetNum() const;
int GetDen() const;
// opérateurs membres de la classe
Rationnel & operator=(const Rationnel & r);
Rationnel operator+(const Rationnel & r) const;
private:
int _num;
int _den; Gritli malek 48
};
Exemple : classe de nombres rationnels

// [Link] : implémentation de la classe


#include "Rationnel.h"
// Constructeurs
Rationnel::Rationnel(int num,int den){
if(den==0) _den=1; // éviter le dénominateur nul
if(den<0){ // placer le signe au numérateur
_den=-den;
_num=-num;
}
}
Rationnel::Rationnel(const Rationnel & r) // constructeur de copie
{
_den=r._den;
_num=r._num;
}

Gritli malek 49
Exemple : classe de nombres rationnels

// Sélecteurs
int Rationnel::GetNum() const { return _num; }
int Rationnel::GetDen() const { return _den; }
// opérateur pour l’affichage d’un rationnel (non membre)
std::ostream & operator<<(std::ostream & flot, const Rationnel & r)
{
flot << [Link]() << "/" << [Link]();
return flot;
}
// opérateur d’affectation (obligatoirement membre)
Rationnel & Rationnel::operator=(const Rationnel & r)
{
_num=r._num;
_den = r._den;
return *this;
}

Gritli malek 50
Exemple : classe de nombres rationnels

// opérateur pour la somme de nombres rationnels (membre)


Rationnel Rationnel::operator+(const Rationnel & r) const
{
Rationnel resultat(_num*r._den+r._num*_den,_den*r._den);
return resultat;
}

// opérateur pour le produit de nombres rationnels (hors classe )


Rationnel operator*(const Rationnel & r1,const Rationnel & r2) const
{
// Attention, cet opérateur n'a pas accès aux membres privés !
Rationnel resultat([Link]()*[Link](),[Link]()*[Link]());
return resultat;
}

Gritli malek 51
Exemple : classe de nombres rationnels

// utilisation de la classe Rationnel


#include "Rationnel.h"
using namespace std;
void main()
{
Rationnel r1(2,3),r2(4,5);
cout << " r1=" << r1;
cout << " r2=" << r2 << endl;
Rationnel r;
r=r1+r2; // equivaut à [Link]=([Link]+(r2))
cout << "r1+r2=" << r << endl;
r=r1*r2; // equivaut à [Link]=(operator*(r1,r2))
cout << "r1*r2=" << r;
}

Gritli malek 52
classe de nombres rationnels : remarques

● Remarquer que la somme ne modifie pas l’objet courant (méthode constante)

● Remarquer que la somme ne modifie pas les lobjets argument (référence constante)

● L'opérateur retourne un objet (différent de l’objet courant et de l’objet argument) de


type rationnel (l'objet resultat)

Gritli malek 53
Classes paramétrées en type (la bibliothèque STL)

● En C++ au sein d’une classe ou d’une fonction, certains types peuvent être passés en
paramètre.

● Le mot clé template est introduit en C++ pour pouvoir paramétrer des types

● Une bibliothèque standard de classes paramétrées en type, libre d’utilisation, est fournie
avec la plupart des compilateurs C++ ((Standard Template Library STL)

● On y (STL) retrouve des classes conteneur paramétrées et des algorithmes paramétrés


(recherche de valeurs dans un conteneur, tris de conteneur …)

● STL fournit une classe string de gestion des chaînes de caractères

Gritli malek 54
Notion de conteneur

Un conteneur est simplement un objet capable de stocker des données avec la


possibilité de changer dynamiquement sa taille

✗ Le conteneur vector : tableau unidimensionnel

✗ Le conteneur list : liste doublement chaînée

✗ Le conteneur stack :pile (structure LIFO)

✗ Le conteneur queue :file (structure FIFO)

✗ Le conteneur deque : structure de données hybride

✗ Le conteneur set : ensemble

✗ Le conteneur map : tableau associatif

Gritli malek 55
Notion de conteneur
● Tous les conteneurs STL sont paramétrés en type. Cela signifie qu’on peut choisir le type
des données stockées dans le conteneur
● On note vector<T> la classe obtenue à partir du conteneur vector pour le type T
#include<iostream> // entête pour les classe istream et ostream
#include<vector> // entête pour le conteneur vector
using namespace std; // la bibliothèque STL utilise le namespace std
int main ( )
{vector<int> tab1(4); // objet de la classe vector<int>
vector<float> tab2(2); // objet de la classe vector<float>
vector<vector<int> > tab3(2); //objet de la classe vector<vector<int> >
Tab1[1=3; // les éléments stockés dans tab1 sont des entiers
Tab2[0]=3.7; // les éléments stockés dans tab2 sont des float
tab3[0]=tab1; // tab3 stocke des tableaux d’entiers (vector<int>)
for(unsigned i=0;i<[Link]();i++) // affichage des éléments de tab1
{
cout << tab1[i] << " ";
}}

Gritli malek 56
Notion de conteneur

● L’accès à une case d’un objet vector<T> se fait simplement avec l’opérateur [

● La classe vector<T> dispose de bien d’autres méthodes utilisables pour changer la


taille, insérer des éléments, supprimer des éléments. Par exemple, on voit
ci-dessusl’utilisation de la méthode vector<T>::size() sur l’objet tab1 .

● L'objectif est de présenter simplement quelques caractéristiques des classes string ,


vector , et list .

Gritli malek 57
Namespace

● Avant d’aller plus loin dans la présentation de la STL, il semble temps d’expliquer ce
que représente un namespace (espace de noms)
● Différentes classes ou fonctions peuvent être regroupées dans un espace de noms
particulier pour éviter certains conflits d’identifiant
● Pour utiliser une classe d’un espace de noms particulier, on doit faire précéder le nom
de la classe du nom de l’espace de noms

#include <iostream>
namespace NA{
void MaFonction()
{
std::cout << "Fonction du namespace NA \n";
}}
namespace NB{
void MaFonction()
{
std::cout << "Fonction du namespace NB \n";
}}
Gritli malek 58
Namespace

● Il n’y a pas de conflit de nom puisque les deux fonctions sont définies dans deux
espaces de noms distincts (les namespaces NA et NB ). On voit aussi que le flot cout
dépend du namespace std .
● Toutes les classes de la STL appartiennent au namespace std . Il convient donc de
préciser ce namespace pour l’utilisation de ces classes
● On ne peut pas utiliser la directive using namespace dans les fichiers d’entête

using namespace NA; // utilise le namespace NA par défaut


Int main( )
{
NA::MaFonction(); // fonction du namespace NA
NB::MaFonction(); // fonction du namespace NB

MaFonction(); // fonction du namespace par défaut


}

Gritli malek 59
Classe string

● N’est pas une classe paramétrée. Elle sert uniquement à gérer des chaînes de
caractère de façon un peu plus souple qu’en langage C

#include<string> // pour la classe string


#include<iostream>
using namespace std;
int main( )
{
string s1("Une chaine"),s2(" Toto");
string s3(s1);
string s4;
s4=s1+s2; // + : concaténation = : affectation
cout << s3 << endl; // insertion possible dans cout
cout << s3[0] << endl; // utilisable comme un tableau : s3[i] de type char
if(s1==s3) cout << "Chaines identiques\n";
if(s1<=s4) cout << "s1<=s4 selon l'ordre lexicographique\n";
}

Gritli malek 60
Classe string

L’exemple suivant illustre les faits suivants : la classe contient

➢ un constructeur sans argument pour créer une chaîne vide

➢ un constructeur avec un argument de type char * pour assurer la compatibilité avec le C

➢ un constructeur de copie pour créer une chaîne identique à une chaîne existante

➢ des opérateurs de concaténation : + et +=

➢ un opérateur d’affectation : =

➢ un opérateur d’indexation [ ] de sorte qu’un objet string se comporte aussi comme un


tableau

➢ les opérateurs de test : == !=

➢ les opérateurs de comparaison selon l’ordre lexicographique (l’ordre des mots dans un
Dictionnaire) : <= < >= >

Gritli malek 61
Classe string

int main( )
{
string s1("abcdefg");
cout << "Longueur = " << [Link]() << endl;
cout << "Capacite = " << [Link]() << endl;
cout << s1 << endl; // s1 = abcdefg
[Link](1,"ABC"); // insère la chaîne "ABC" à l'indice 1
cout << s1 << endl; // s1 = aABCbcdefg
[Link](2,4,'a'); // insère 4 fois la lettre 'a' à l'indice 2
cout << s1 << endl; // s1 = aAaaaaBCbcdefg
[Link](0,1); // supprime 1 caractère à l'indice 0
cout << s1 << endl; // s1 = AaaaaBCbcdefg
[Link](2,5); // supprime 5 caractères à l'indice 2
cout << s1 << endl; // s1 = Aabcdefg
const char * ptr=s1.c_str(); // possibilité d'obtenir un pointeur compatible const char *
}

Gritli malek 62
Conteneurs vector<T>
● La classe paramétrée vector<T> permet de générer des objets tableaux pouvant
stocker des objets de n’importe quel type

#include<iostream>
#include<vector>
using namespace std;
void main(void)
{
vector<int> t1(4,-2),t2(5); // t1=-2,–2,–2,-2 t2=0,0,0,0,0
vector<float> t3(2),t4(4,1.2); // t3=0,0 t4=1.2,1.2,1.2
T1[0]=5; //t1=5,–2,–2,-2
T4[2]=-2.3; //t4=1.2,1.2,-2.3
t2=t1; // possible car t1 et t2 de la même classe
t3=t4; // possible car t3 et t4 de la même classe
// t1=t4; impossible car t1 et t4 de types différents
vector<int> t5(t1); // t5=5,–2,–2,-2
vector<float> t6(t3); // t6=1.2,1.2,-2.3

//vector<float> t7(t1); impossible car t1 n’est pas de la classe vector<float>


}
Gritli malek 63
Conteneurs vector<T>

● La classe vector<T> dispose d’un constructeur à un argument (la taille initiale du


vecteur)

● La classe vector<T> dispose d'un constructeur à deux arguments (taille et valeurs


initiales du vecteur

● La classe dispose aussi d’un constructeur de [Link], les copies ne sont


possibles qu’entre objets de même classe !

● La classe dispose d’un opérateur d’affectation. Là encore, l’affectation n’est possible


qu’entre objets de même classe

● L’opérateur d’indexation [ ] donne accès aux éléments du tableau

Gritli malek 64
Les itérateurs
● Les classes conteneur implémentent la notion d’itérateur qui généralise la notion de
pointeur
● Les opérateurs ++ et –sont surchargés sur les itérateurs pour passer à l’emplacement
suivant ou précédent du conteneur
● L’opérateur * permet l’indirection sur un itérateur (comme sur un pointeur)
● Grâce aux itérateurs, les algorithmes s’appliquant aux conteneurs sont moins
dépendants de leur structure interne.
● Les classes conteneurs contiennent des classes membres publiques pour instancier des
itérateurs adaptés. Il y a deux classes d’itérateurs par conteneur : les itérateurs
constants et les itérateurs non constants. Les premiers permettent de parcourir un
conteneur sans pouvoir en modifier le contenu.
int main( )
{vector<int> t1(4);
vector<int>::iterator it; // it est un iterateur sur vector<int>
for(it=[Link]();it!=[Link]();it++) (*it)=2;
for(unsigned i=0;i<[Link]();i++) t1[i]=2;
}
Gritli malek 65
Les itérateurs

● Les méthodes begin() et end() retournent des itérateurs. La première


méthoderetourne un itérateur sur le premier élément du conteneur. La seconde
pointe juste après le dernier élément.
● L’exemple suivant illustre les capacités d’insertion et de suppression dans un vecteur
(voire dans un conteneur différent)
int main( )
{vector<int> t1(3,2); //t1=2,2,2
cout << "Taille = "<< [Link]() << endl;
// Taille=3
t1.push_back(3);
[Link](6,-2);
//t1=2,2,2,3
//t1=2,2,2,3,-2,-2
[Link]([Link](),2,-3); //t1=-3,-3,2,2,2,3,-2,-2
[Link]([Link]()+3,-1); //t1=-3,-3,2,-1,2,2,3,-2,-2
[Link](&t1[0],-6); //t1=-6,-3,-3,2,-1,2,2,3,-2,-2
[Link]([Link]()+2,[Link]()+4); //t1=-6,-3,-1,2,2,3,-2,-2
[Link]([Link]()+3); //t1=-6,-3,-1,2,3,-2,-2
t1.pop_back(); //t1=-6,-3,-1,2,3,-2
} Gritli malek 66
Les itérateurs

● Ajout/retrait en fin = push_back et pop_back


● Ajout/retrait en tête = push_front et pop_front
● Premier/dernier élément : front et back
● Changement de dimension : resize (nouvelle taille, valeur des cases complémentaires ).
● Insertion : plusieurs méthodes.
-insert (itérateur, nombre d’ajouts, valeur insérée) : plusieurs cases insérées
-insert (itérateur, valeur insérée) : une seule valeur insérée
-Remarquer qu’un pointeur sur entier peut être converti en itérateur sur entier.
● Suppression : plusieurs méthodes.
-erase (itérateur debut,iterateur fin) : supprime les cases entre ces itérateurs
-erase (itérateur) : supprime la case pointée par l’itérateur

Gritli malek 67
Conteneurs list<T>

● Autres conteneurs ont de nombreux points communs avec ce qu’on a déjà vu


● L’opérateur [ ] n’est évidemment pas défini pour les listes chaînées.
int main( )
{
list<int> l1; //liste vide d'entiers
list<float> l2,l4; // liste vide de flottants

l1.push_front(12); //ajout en tête


l1.push_back(13); //ajout en fin
l1.push_front(-3); //ajout en fin
l1.push_front(7); //l1=7,-3,12,13
l2.push_back(2.3);
l2.push_back(2.7);
l2.push_back(-1.2); //l2=2.3,2.7,-1.2
list<int> l3(l1); //l3=7,-3,12,13
cout << "Taille = "<< [Link]() << endl;

Gritli malek 68
Conteneurs list<T>

l4=l2; //l4=2.3,2.7,-1.2
[Link]([Link](),2,-3); //l1=-3,-3,7,-3,12,13
cout << [Link]() << [Link]() << endl; // -3 13
list<int>::iterator it=[Link]();
while(it!=[Link]()){
if((*it)==-3) // suppression des noeuds valant -3
{
it=[Link](it);
}
else it++;
}
// affichage de la liste l1
for(it=[Link]();it!=[Link]();it++) cout << (*it) << " ";
}

Gritli malek 69
La composition
● Certains objets vont être composés d’objets d’autres classes
● On peut décrire la composition de deux manières :
→ soit les objets composants apparaissent comme attributs des objets composites
→ soit une relation de composition (lien avec losange côté composite) lie les classes
composant et composite.

Gritli malek 70
Objets composants comme attributs d’une
classe

//point.h : entête de la classe point (classe composite)


#ifndef _POINTRAT__
#define _POINTRAT__
#include "rationnel.h" // inclusion de l’entête de la classe composant
class Point
{
public:
Point();
Point(const Rationnel & x, const Rationnel & y);
Point(const Point & p);
Rationnel GetX() const;
Rationnel GetY() const;
private:
Rationnel _x; // objets membres de type Rationnel
Rationnel _y;
};
#endif

Gritli malek 71
Objets composants comme attributs d’une
classe

//[Link] (implémentation des méthodes de la classe Point)


#include "point.h"
Point::Point():_x(0,1),_y(0,1)
{
}
Point::Point(const Rationnel & x, const Rationnel & y):_x(x),_y(y)
{
}
Point::Point(const Point &p):_x(p._x),_y(p._y)
{
}
Rationnel Point::GetX() const {
return _x; }
Rationnel Point::GetY() const {
return _y; }

Gritli malek 72
Attributs de type tableau d'objets

● Pour la classe Point , on peut voir les coordonnées comme un tableau d'objets
Rationnel à deux cases. Cette autre réalisation est mise en œuvre dans la classe
PointTab ci-dessous.

// pointtab.h
#ifndef _POINTTAB__
#define _POINTTAB__
#include "rationnel.h"
class PointTab
{
public:
PointTab(const Rationnel & x, const Rationnel & y);
Rationnel GetX() const;
Rationnel GetY() const;
private:
Rationnel _XY[2];
// tableau de deux objets Rationnel
};
#endif

Gritli malek 73
Attributs de type tableau d'objets

//[Link]

#include "PointTab.h"

PointTab::PointTab(const Rationnel & x, const Rationnel & y)


{
_XY[0]=x;
_XY[1]=y;
}

Rationnel PointTab::GetX() const { return _XY[0]; }

Rationnel PointTab::GetY() const { return _XY[1];}

Gritli malek 74
Objet membre de type vector<T>

#ifndef _POINTTAB__
#define _POINTTAB__
#include "rationnel.h"

#include<vector>
class PointTab
{
public:
PointTab(const Rationnel & x, const Rationnel & y);
Rationnel GetX() const;
Rationnel GetY() const;
private:
std::vector<Rationnel> _XY;
};
#endif

Gritli malek 75
Objet membre de type vector<T>

//[Link]
#include "PointTab.h"
// Attention ! Utiliser la liste d'initialisation pour initialiser le vecteur à la taille 2
PointTab::PointTab(const Rationnel & x, const Rationnel & y):_XY(2)
{
_XY[0]=x;
_XY[1]=y;
}

Rationnel PointTab::GetX() const {return _XY[0]; }

Rationnel PointTab::GetY() const { return _XY[1];}

Gritli malek 76
La spécialisation en C++

● Une classe de dés à 6 faces peut être vue comme une spécialisation d’une classe dedés à n
faces : il suffit de fixer le nombre de faces

● Certain vocabulaire associé à cette notion de spécialisation/généralisation (spécialisation,


dérive,hérite, super-classe ...)

Gritli malek 77
La spécialisation en C++

// DeNFaces.h : entête d’une classe de dés à N faces


#ifndef __CLS_DENFACES__
#define __CLS_DENFACES__
class DeNFaces
{
public:
DeNFaces(unsigned nbFaces=6);
unsigned GetNombreDeFaces() const;
void Lancer();
unsigned GetValeur() const;
private:
unsigned _valeur; // valeur du dé à un instant
unsigned _nbFaces; // nombre de faces du dé
};
#endif

Gritli malek 78
La spécialisation en C++

// De6.h : fichier d’entête de la classe De6 (dérivée de DeNFaces)


#ifndef __CLS_DE6__
#define __CLS_DE6__
#include "DeNFaces.h"

class De6:public DeNFaces // la classe De6 dérive de DeNFaces


{
public:
De6(); // constructeur de la classe dérivée
};
#endif

Gritli malek 79
La spécialisation en C++

// [Link] :implémentation de la classe spécialisée


#include "De6.h"
De6::De6():DeNFaces(6)
{}
// [Link] : utilisation de la classe sépcialisée
#include "De6.h"
#include<iostream>
using namespace std;
void main(void)
{
DeNFaces de1(9); // dé à 9 faces
De6 de2; // dé à 6 faces
cout << [Link]() << "\n";
cout << [Link]() << "\n";
[Link]();
[Link](); // appel de méthode héritée
cout << [Link]() << endl;
cout << [Link]() << endl; // appel de méthode héritée
}

Gritli malek 80
La spécialisation en C++

Gritli malek 81
La spécialisation en C++

#ifndef __CLS_DE6COUL__
#define __CLS_DE6COUL__

#include "De6.h"

class De6Couleur:public De6


{
public:
De6Couleur(unsigned couleur);
unsigned GetCouleur() const;
private:
unsigned _couleur;
};

#endif

Gritli malek 82
La spécialisation en C++

// [Link] : implémentation de la classe de


De6Couleur

#include "De6Couleur.h"

De6Couleur::De6Couleur(unsigned c):De6(),_couleur(c)
{
}

unsigned De6Couleur::GetCouleur() const


{
return _couleur;
}

Gritli malek 83
Les conversions de type entre sous-classe
et super-classe.
● En C++, les conversions de type entre sous-classe et super classe sont légales. Par
exemple, on peut convertir un objet De6 en objet DeNFaces .On peut de la même
manière convertir un objet de la classe De6Couleur en un objet de la classe
DeNFaces .
● Lorsque l’on convertit un dé coloré en dé (sans couleur) on perd l’information de
couleur (transformation dégradante ,perte de la la partie spécifique d’une classe
dérivée)

#include "De6Couleur.h"

#define rouge 5
int main(int)
{
De6Couleur de6r(rouge);
De6 de6;
DeNFaces denf(9);
((DeNFaces)de6r).GetValeur(); // cast De6Couleur -> DeNFaces
((DeNFaces)de6).GetValeur(); // cast De6 -> DeNFaces
}

Gritli malek 84
La conversions de type entre sous-classe et
super-classe.
● Conversion sous-classe * -> super-classe * : les conversions de pointeurs entre
sous-classe et super-classe sont également légales en C++

#include "De6Couleur.h"
#define rouge 5
int main( )
{
De6Couleur de6r(rouge);
De6 *ptrDe6;
DeNFaces *ptrDeNF;
ptrDe6=&de6r; // De6Couleur * -> De6 *
ptrDe6->Lancer();
ptrDeNF=ptrDe6; // De6 * -> DeNFaces *
ptrDeNF->Lancer();
}

Gritli malek 85
Le polymorphisme et les méthodes virtuelles
● En C++, le polymorphisme représente aussi la possibilité pour des objets d'une
descendance à répondre différemment lors de l'appel d'une méthode de même nom.
Le polymorphisme est donc lié dans ce cas aux hiérarchies de classes.
#include "Rectangle.h"
#include "Cercle.h"
#include<iostream>
using namespace std;
int main( )
{
Fenetre f; // objet fenêtre graphique
ObjetGraphique * tab[3];
tab[0]=new Cercle;
tab[1]=new Rectangle;
tab[2]=new Polygone ;
// méthode de la classe ObjetGraphique
// Invoquée car la méthode n’est pas
//polymorphe par défaut
tab[0]->DessinerDans(f);
tab[1]->DessinerDans(f);
tab[2]->DessinerDans(f);
for(int i=0;i<3;i++) delete tab[i];
Gritli malek 86
}
Le polymorphisme et les méthodes virtuelles
● Si la méthode DessinerDans() n'est pas déclarée virtual dans la classe de base
ObjetGraphique , c'est nécessairement la méthode de la classe ObjetGraphique qui va
être invoquée, même si l'objet pointé est de la classe Rectangle ou Cercle
● Le C++ laisse la possibilité de mettre en place ou non le polymorphisme (via les
méthodes virtuelles) pour des raisons de performance. Car la définition de méthodes
virtuelles (voir ci-dessous) implémente une table d’indirection supplémentaire qui
augmente la taille du code et ralentit l’exécution
#ifndef __RECTANGLE__
#ifndef __OBJETGRAPHIQUE__ #define __RECTANGLE__
#define __OBJETGRAPHIQUE__ #include "ObjetGraphique.h"
#include "Fenetre.h" class Rectangle: public ObjetGraphique
class ObjetGraphique {
{ public:
public: Rectangle();
ObjetGraphique(); virtual void DessinerDans(Fenetre &)
virtual void DessinerDans(Fenetre &) const; const;
}; };
#endif #endif

Gritli malek 87
Les classes abstraites

● Toutes les formes géométriques devront pouvoir réagir à des mêmes demandes (mêmes
méthodes (dessiner , supprimer ...)).

● L’interface de la classe de base ObjetGraphique va contenir tout ce que les différentes


formes géométriques seront capable d’exécuter

● la classe ObjetGraphique va être une classe dite [Link] contiendra des fonctions
virtuelles pures, c’est-à-dire des fonctions membres qui ne seront pas définies

● Les fonctions virtuelles pures n’ont pas de code et ont un prototype qui se termine par =0

● Une classe qui contient au moins une fonction virtuelle pure est dite abstraite. On ne peut
alors pas créer d’objet d’une telle classe (mais on pourra créer des objets d’une classe
dérivée).

Gritli malek 88
Les classes abstraites
#ifndef __OBJETGRAPHIQUE__
#define __OBJETGRAPHIQUE__
#include "Fenetre.h"
#include "Point.h"
class ObjetGraphique // classe abstraite
{
public:
ObjetGraphique();
virtual ~ObjetGraphique(); // destructeur virtuel

// fonctions virtuelles pures


virtual void DessinerDans(Fenetre &) const =0;
virtual void Deplacer(int DeltaX, int DeltaY) =0 ;
virtual void Contient(const Point &) const =0 ;
...

};
#endif

Gritli malek 89
Les classes abstraites
● On ne peut pas créer d’objet de la classe ObjetGraphique , mais en revanche, on
peut faire un conteneur d’objets graphiques (objets hétérogènes).
● En résumé, une classe abstraite décrit ce qu’on attend d’une classe (comportement)
sans savoir comment cela va être réalisé. En fait le comportement final est
programmé dans la ou les classes descendant de la classe abstraite.

int main()
{
vector<ObjetGraphique *> FormesGraphiques;
Fenetre F ;
FormesGraphiques.push_back(new Rectangle(10,10,40,55));
FormesGraphiques.push_back(new Ellipse(60,60,140,155));
FormesGraphiques.push_back(new Rectangle(30,30,40,40));
for(int i=0;i<[Link]();i++)
FormesGraphiques[i]->DessinerDans(F);
for(int i=0;i<[Link]();i++) delete FormesGraphiques[i];
}

Gritli malek 90
Visibilité des membres d’une classe

● Les membres public d’une classe sont accessibles à la fois aux objets de la classe
ainsi qu’à l’utilisateur de la classe

● L’encapsulation des données d’une classe est possible grâce au mot-clé private,
l’utilisateur ne peut pas agir directement sur les données membres d’un objet

● Comment des fonctions non membres d’une classe, ou comment d’autres classes,
peuvent avoir également accès à la partie encapsulée d’une classe donnée ?

● Un membre protégé (mot-clé protected ) est comme un membre privé (non


accessible par l’utilisateur) mais reste accessible pour une classe dérivée

Gritli malek 91
Mot-clé friend : fonctions amies
● On illustre ceci en reprenant la classe Rationnel vue dans la section sur les
opérateurs en définissant les opérateurs + et * comme des fonctions non membres
de la classe Rationnel mais amis de cette classe. Les membres _den et _num
deviennent accessibles à ces fonctions non membres.
class Rationnel
{
// fonctions non membres mais amies de la classe
friend Rationnel operator+(const Rationnel & r1,const Rationnel & r2);
friend Rationnel operator*(const Rationnel & r1,const Rationnel & r2);
...
};
#include "Rational.h"
#include <iostream>
Rationnel operator+(const Rationnel & r1,const Rationnel & r2){
Rationnel local(r1._num*r2._den+r2._num*r1._den,r1._den*r2._den);
return local;
}
Rationnel operator*(const Rationnel & r1,const Rationnel & r2){
Rationnel local(r1._num*r2._num,r1._den*r2._den);
return local;
} Gritli malek 92
Mot-clé friend :classes amies

● La classe B est déclarée amie de la classe A . Cela signifie que toutes les méthodes
de la classe B peuvent avoir accès à la partie privée de la classe A
● Il est préférable de chercher à éviter autant que possible d’utiliser l’amitié (pas
sécurisé du tout )

#include "B.h"

class A
{
public:
friend B; // la classe B est amie de la classe A
...

};

Gritli malek 93
Membres protégés (mot-clé protected )

● Un membre (donnée ou méthode) déclaré protected est accessible par les objets de
la classe mais pas par l’utilisateur de la classe (comme pour un membre privé)
#ifndef ___CLS_DERIVEE__
#ifndef ___CLS_BASE__ #define ___CLS_DERIVEE__
#define ___CLS_BASE__ #include "Base.h"
class Base class Derivee : public Base
{ {
public: public:
Base(); Derivee();
MethodeB(); MethodeD();
protected: protected:
bool _indPProtegeB; bool _indProtegeD;
void FonctionProtegeeB(); void FonctionProtegeeD();
private: private:
int _dPriveeB; int _dPriveeD;
void FPriveeB(); void FPriveeD();
}; };
#endif #endif

Gritli malek 94
Membres protégés (mot-clé protected )

● Dans une méthode privée ou protégée de le classe Derivee, on peut accéder à la


partie publique de la classe de base (normal) et à la partie protégée de la classe de
base.

void Derivee::FpriveeD() // implémentation de la classe Derivee


{
MethodeB(); // possible (car publique)
MethodeD(); // possible (car publique)

_indPProtegeB=true; // possible (car membre protégé de Base)


//_dPriveeB=3; // non possible (car membre privé de Base)
FonctionProtegeeB(); // possible (car fonction protégée de Base)
//FpriveeB(); // non possible (car fonction privée de Base)
}

Gritli malek 95
Formes de dérivation possibles

● La dérivation publique ( class Derivee : public Base ). C’est le mode de dérivation le


plus courant
● Il existe aussi une dérivation privée ( class Derivee : private Base ) et protégée
( class Derivee : protected Base )
● La visibilité des membres de la classe de base dépend du mode de dérivation (FMA
signifie Fonctions Membres et Amies)

Gritli malek 96
Phénomènes de masquage
● La classe dérivée comporte, comme la classe de base, une méthode Affiche()
● Sur un objet Derivee , la méthode Affiche() appelée est nécessairement celle de la classe
dérivée. On dit qu’elle masque celle de la classe de base
● Pour éviter le masquage ,il faut préciser le nom de la classe lors d’un appel de méthode

class Derivee:public Base


class Base {
{ public:
public: Derivee();
Base(); void Affiche() const;
void Affiche() const; void AfficheD() const;
void AfficheB() const; ...
... };
};

Gritli malek 97
Phénomènes de masquage

#include "derivee.h"
int main()
{
Derivee D;
Base B;
[Link]();
[Link]();
[Link]();
[Link](); // Derivee::Affiche() masque ici
[Link]::Affiche(); // appel explicite de la méthode de la classe de base
[Link]::Affiche() ; // équivalent à [Link]();

[Link]::AfficheB(); // équivalent à [Link]() ;

// [Link]::AfficheD(); n’a en revanche pas de sens


}

Gritli malek 98
Classes avec données en profondeur

● En C, on demande de la mémoire avec la fonction malloc() et on la libère avec la


fonction free() .En C++, l'allocation dynamique de mémoire s'effectue avec les
opérateurs new et delete .

T * ptr;
ptr = new T; // allocation d'un objet (ou une variable) de type T
ptr = new T[n]; // allocation d'un tableau de n objets de type T

● Dans les deux cas, le résultat de l'opération est une adresse.

delete ptr;
delete [] ptr;

● Cette seconde syntaxe assure que tous les destructeurs des objets du tableau sont
appelés avant que le tableau soit libéré. Ceci est important si les objets du tableau
ont des données en profondeur

Gritli malek 99
Classes avec données en profondeur
● Un objet composant n'est pas réellement dans la classe mais est connu via un
pointeur
● Les données de l'objet composant n'étant plus dans l'objet composite, on dit que les
données sont en profondeur (obtenues indirectement par un pointeur)

#ifndef _POINT__
#define _POINT__
#include "rationnel.h"
class Point
{public:
Point(const Rationnel & x, const Rationnel & y);
Point(const Point & p);
~Point(); //destructeur
Point & operator=(const Point & p);
Rationnel GetX() const;
Rationnel GetY() const;
private:
Rationnel * _XY; // pointeur sur le composant
};
#endif

Gritli malek 100


Classes avec données en profondeur

//[Link] // constructeur de copie


#include "Point.h" Point::Point(const Point &p){
#include <process.h> _XY=new Rationnel[2];
#include <iostream> if(_XY==NULL){
using namespace std; cerr << "echec d'allocation.";
Point::Point(const Rationnel & x, exit(2);
const Rationnel & y){ }
_XY=new Rationnel[2]; _XY[0]=p._XY[0];
if(_XY==NULL){ _XY[1]=p._XY[1];
cerr << "echec d'allocation de mémoire."; }
exit(2);
}
_XY[0]=x;
_XY[1]=y;
}

Gritli malek 101


Classes avec données en profondeur

● Le constructeur a pour rôle d’allouer de la mémoire et le destructeur (méthode ~Point )


celui de libérer la zone mémoire allouée par le constructeur.
// destructeur : méthode appelée automatiquement juste avant la disparition de l’objet
Point::~Point()
{
delete [] _XY;
}

Point & Point::operator=(const Point &p)


{
_XY[0]=p._XY[0];
_XY[1]=p._XY[1];
return *this;
}
Rationnel Point::GetX() const {
return _XY[0]; }
Rationnel Point::GetY() const {
return _XY[1]; }

Gritli malek 102


classe d'objets tableaux

#ifndef __CLS_TABLEAU__
#define __CLS_TABLEAU__
class Tableau
{
public:
Tableau(unsigned taille=5);
Tableau(const Tableau & tab);
Tableau & operator=(const Tableau & tab);
virtual ~Tableau();
double & operator[](unsigned idx);
double operator[](unsigned idx) const;
void NouvelleTaille(unsigned taille);
unsigned GetTaille() const;
unsigned GetCapacite() const;
private:
double * _tab;
unsigned _capacite;
unsigned _taille;
};
#endif
Gritli malek 103
Classe d'objets tableaux

#include "Tableau.h"
#include<process.h>
#include<iostream>
using namespace std;
Tableau::Tableau(unsigned taille):_taille(taille),
_capacite(2*taille), _tab(new double[2*taille])
{
if(_tab==NULL){
cerr << "echec d'allocation";
exit(2);
}
// les éléments du tableau sont initialisés à 0
for(unsigned i=0;i<_taille;i++) _tab[i]=0;
}
Tableau::~Tableau(){
delete [] _tab;
}

Gritli malek 104


Classe d'objets tableaux

// constructeur de copie
Tableau::Tableau(const Tableau & t):_taille(t._taille),
_capacite(t._capacite),_tab(new double[t._capacite])
{
if(_tab==NULL){
cerr << "echec d'allocation";
exit(2);
}
for(unsigned i=0;i<_taille;i++) _tab[i]=t._tab[i];
}
double & Tableau::operator [](unsigned idx)
{
if(idx>=GetTaille()){
// verification d’indice
cerr << "indice incorrect!";
exit(2);
}
return _tab[idx];
}

Gritli malek 105


Classe d'objets tableaux

double Tableau::operator [](unsigned idx) const


{
if(idx>=GetTaille()) {
cerr << "indice incorrect!";
exit(2);
}
return _tab[idx];}
// opérateur d'affectation
Tableau & Tableau::operator=(const Tableau & t)
{if(_capacite<t._taille) // réallocation si capacité insuffisante
{_capacite=2*t._taille;
double * temp=new double[_capacite];
if(_tab==NULL){cerr << "echec d'allocation";
exit(2);}
delete [] _tab;
_tab=temp;
}
_taille=t._taille;
for(unsigned i=0;i<t._taille;i++) _tab[i]=t._tab[i];
return *this;
} Gritli malek 106
Classe d'objets tableaux
unsigned Tableau::GetTaille() const {
return _taille; for(unsigned i=_taille;i<taille;i++)
} _tab[i]=0;
unsigned Tableau::GetCapacite() const { _taille=taille;
return _capacite; }
} }
// méthode permettant le changement de taille
void Tableau::NouvelleTaille(unsigned taille)
{if(taille<=GetTaille())
_taille=taille;
else
{if(taille>_capacite)
{_capacite=2*taille;
double * temp=new double[_capacite];
if(_tab==NULL){
cerr << "echec d'allocation";
exit(2);}
for(unsigned i=0;i<_taille;i++) temp[i]=_tab[i];
delete [] _tab;
_tab=temp;
}
Gritli malek 107
Fonction retournant une référence

double & Tableau::operator [](unsigned idx)


{
return _tab[idx];
}

● Il faut noter que l’opérateur d’indexation [] retourne une référence. Ainsi on peut
modifier un élément du tableau via cet opérateur
● Il faut également remarquer que la classe dispose de deux opérateurs d’indexation.
Un opérateur constant qui peut être appelé sur des objets constants (c’est-à-dire des
tableaux constants) (renvoie donc le contenu de la case par valeur) et un opérateur
d'indexation (non constant) quant à lui retourne une référence
● On ne peut pas appeler sur un objet constant que les méthodes constantes : ici il
s’agit de GetTaille(), GetCapacite() et un opérateur []
● Il reste néanmoins préférable d’utiliser la bibliothèque STL plutôt que de redéfinir des
conteneurs.
Gritli malek 108
Les fonctions paramétrées en type

● On peut paramétrer les types, on n’a qu’une seule définition à donner. C’est ce que
permet la notation template
● La notation template<class T> indique au compilateur que T est un paramètre de
type (type primitif,structuré ou classe). La fonction permute peut alors être appelée
avec deux arguments d'un type quelconque,dans la mesure où les deux arguments
sont du même type

template<class T> void permute(T & a,T & b)


{
T c=a; a=b; b=c;
}
int main()
{
int x=2,y=3;
double u=1.3,v=1.7;
permute(x,y); // T=int
permute(u,v); // T=double
}

Gritli malek 109


Les fonctions paramétrées en type

● Dans un patron de fonctions, on peut aussi faire apparaître des arguments typés
normalement. Ces paramètres sont parfois appelés paramètres expression

template <class T> void tri(T * tab,unsigned taille)


{
// algorithme du tri d'un tableau de taille éléments de type T;
// ... //
}

● Un patron de fonctions définit une famille de fonctions ayant toutes le même


algorithme mais pour des types différents
● La fonction min() obtenue à partir dupatron a du sens pour tous les types
élémentaires sauf pour le type char* . Nous avons donc défini une spécialisation pour
le type char * . Cette spécialisation remplace le code que génèrerait le patron.

Gritli malek 110


Les fonctions paramétrées en type

#include<iostream>
#include<string.h>
using namespace std;
//patron de fonctions
template <class T> T min(T a,T b){ return a<b ? a : b; }
//spécialisation du patron pour le type char *
char * min(char * s1,char * s2)
{
if(strcmp(s1,s2)<0)
return s1;
else
return s2;
}
int main()
{
int a=1,b=3;
char chaine1[]="coucou",chaine2[]="salut";
cout << min(a,b); //générée à partir du patron
cout << min(chaine1,chaine2); //spécialisation
}
Gritli malek 111
Classes paramétrées en type

● Les conteneurs STL sont des classes paramétrées en type


● On va réécrire la classe Point de sorte de pouvoir instancier des points dont les coordonnées
soient d'un type choisi par l'utilisateur
//fichier point.h
#ifndef __POINT_TEMPLATE__
#define __POINT_TEMPLATE__
template<class T>
class Point // T est un paramètre de type
{
public:
Point<T>(){ _XY[0]=0; _XY[1]=0;}
Point<T>(const T & x,const T & y)
{
_XY[0]=x;
_XY[1]=y;
}
Point<T>(const Point<T> & p)
{
_XY[0]=p._XY[0];
_XY[1]=p._XY[1];
Gritli malek 112
}
Classes paramétrées en type

Point<T> & operator=(const Point<T> & p)


{
_XY[0]=p._XY[0];
_XY[1]=p._XY[1];
return *this;
}
void SetX(const T & x) { _XY[0]=x; }
void SetY(const T & y){ _XY[1]=y; }
const T GetX() const { return _XY[0]; }
const T GetY() const { return _XY[1]; }
private:
T _XY[2]; // tableau de 2 éléments de type T
};
#endif
int main()
{
Point<int> p1(12,16); // point à coordonées entières
Point<double> p2(1.3,4.6); // point à coordonnées rationnelles
}

Gritli malek 113


Enumérations au sein des classes

● Jour est un type et {lundi,mardi …} sont des valeurs pour une variable de type jour

enum jour{lundi,mardi,mercredi,jeudi,vendredi,samedi,dimanche};

● En C++, on peut définir des énumérations au sein des classes.

class Calendrier
{
public:
enum jour{lundi,mardi,mercredi,jeudi,vendredi,samedi,dimanche};
Calendrier();
//...
};
Calendrier::jour j=Calendrier::mardi;
● Le type jour, ainsi que ses valeurs possibles, appartiennent à la classe Calendrier

Gritli malek 114


La déclaration d'une classe au sein d'une
classe
● C'est la technique utilisée par les itérateurs dans la bibliothèque STL
class vecteur{
public:
class iterateur{
public:
iterateur();
...
};
vecteur(int taille);
iterateur begin() const;
int & operator[](int idx);
};
● La classe iterateur est définie dans la classe vecteur. L'utilisation est la suivante.

vecteur v1(3);
vecteur::iterateur i=[Link]();

Gritli malek 115


La hiérarchie des classes de flots
● Une vue (très partielle) de la hiérarchie des classes de gestion des flots entrée/sortie est la
suivante.

● La classe ios est la classe de base pour tous les flots.


● La classe istream est destinée aux flots d'entrée et ostream aux flots de sortie.
● cin et cout sont des objets globaux instanciés à partir de ces classes (istream, ostream).
● La la classe fstream permet la gestion des fichiers. On voit qu'elle dérive à la fois des deux
classes istreamet ostream .

Gritli malek 116


La classe ios
● ios ::open_mode :énumération définie dans la classe ios . Associée en particulier aux
modesd'ouverture des fichiers. Les valeurs possibles sont :

➢ ios::in : ouverture en lecture

➢ ios::out : ouverture en écriture

➢ ios::app : ouverture en ajout (mode append)

➢ ios::binary : ouverture en mode binaire. (Par défaut, on ouvre le


fichier en mode texte.)

● Ces modes peuvent être combinés par l'opérateur |, par exemple ios::out|ios::app .

Gritli malek 117


La classe ostream
● ostream :: put (char) : sortie non formatée d'un caractère
● ostream::write(char *,int n) : sortie de n caractères
● ostream::operator<<( ) : insertion format ée dans le flot pour certains types de base
( char , int …)

#include<fstream>
#include<iostream>
using namespace std;
int main()
{
[Link]('a');
[Link]('\n');
char str[]={'a','b','c',0,'f','g','h'};
cout << str << endl;
[Link](str,7);
}

Gritli malek 118


La classe ostream

● Le format de sortie peut être modifié par certaines méthodes de la classe


ainsi que par des manipulateurs de flot (ci-dessous, endl est un
manipulateur de flot)

int main()
{
float f1=1.3698;
cout << f1 << endl; // sortie formatée -> 1.3698
[Link](3); // changement de la précision
cout << f1 << endl; // sortie formatée -> 1.37
}

Gritli malek 119


La classe istream

● istream::get(char &) : lecture d'un caractère


● istream::getline(char * pStr,int nbCarac,char delim='\n') :lecture d'une suite d'au plus
nbCarac caractères. La lecture s'arrête aussi si le caractère délimiteur (par défaut
'\n') est rencontré.
● istream::read(char * pStr,int nbCarac) : lecture de nbCarac caractères.
● istream::operator>>( ) : lecture formatée pour les types de base.
//... inclusion des fichiers ...
using namespace std;
void main()
{
char tampon[30];
string str;
[Link](tampon,30,'\n'); //la lecture s'arrête sur retour chariot
cin >> str; // la lecture s'arrête sur les espaces
cout << tampon << endl;
cout << str << endl;
}
Gritli malek 120
Les fichiers
● Les fichiers peuvent être gérés par des flots de type fstream (file stream)

● La classe fstream hérite des méthodes des classes istream et ostream , donc de
celles présentées précédemment

➢ fstream::fstream() : crée un objet non connecté à un fichier


➢ fstream::fstream(char * nomFichier, ios::open_mode ) : ouvre un fichier
➢ fstream::~fstream() : fermeture d'un fichier ouvert
➢ fstream::open(char *, ios::open_mode) : ouvre un fichier
➢ fstream::close() : ferme le fichier en cours

Gritli malek 121


Les fichiers
● Voici les modes d'ouverture :( ios::open_mode)
ios::out (w)
ios::in (r)
ios::out|ios::app (a)
ios::in|ios::out (r+)
ios::in|ios::binary (rb)
ios::out|ios::binary (wb)
ios::out|ios::app|ios::binary (ab)

Gritli malek 122


Les fichiers
int main()
{
char tampon[150];
// création ou remplacement du fichier
fstream f("[Link]",ios::out);
// écriture dans le fichier
f<<"test \t" << 1 << "\t"<< 1.5 << endl;
[Link]();
// réouverture du fichier en mode ajout
[Link]("[Link]",ios::out|ios::app);
f<<"ajout \t" << 2 << "\t"<< 2.5 << endl; // écriture dans le fichier
[Link]();
// ouverture en mode lecture
[Link]("[Link]",ios::in);
while(![Link]())
{
[Link](tampon,150);
cout << tampon << endl;
}
}

Gritli malek 123


L'accès direct dans les fichiers
● La classe fstream hérite de méthodes de positionnement.

● Pour la lecture (hérité de istream) :

➢ fstream::seekg(déplacement, position) : positionne le pointeur


➢ fstream::tellg() : retourne la position courante

● Idem pour l'écriture (hérité de ostream) :

➢ fstream::seekp(déplacement, position)
➢ fstream::tellp()

Gritli malek 124


L'accès direct dans les fichiers
● La classe ios définit pour cela des valeurs ios::beg (début du flot), ios::end (fin du
flot), ios::cur (position courante). Il est à noter que la lecture ou l'écriture font évoluer
la position courante.
int main() //positionne au 3ième du début
{ [Link](2*sizeof(char),ios::beg);
char tampon[150]; [Link](tampon,150);
// ouverture en écriture cout << tampon <<endl;
fstream f("[Link]",ios::out); // déplace de 3 caractères
for(int i=0;i<3;i++) f<<"ligne numero "<< i << "\n"; [Link](3*sizeof(char),ios::cur);
// instant t1 [Link](tampon,150);
[Link](0,ios::beg); //positionne en début cout << tampon <<endl;
f<<"premiere ligne \n";
[Link](); }
// instant t2
// ouverture en lecture
[Link]("[Link]",ios::in);

Gritli malek 125


Les changements de type
// [Link]
Entier::Entier()
#ifndef __CLS_ENTIER__ {
#define __CLS_ENTIER__ cout << "constructeur Entier::Entier()"<<endl;
class Entier _data=0;
{ }
public: Entier::Entier(int val) {
Entier(); cout << "constructeur Entier::Entier(int)"<<endl;
Entier(int val); _data=val;
Entier(float val); }
Entier & operator=(const Entier & Entier::Entier(float val)
e); {
int Get() const; cout << "constructeur Entier::Entier(float)"<<endl;
private: _data=(int)val;
int _data; }
}; // opérateur + non membre
Entier operator+(const Entier & Entier operator+(const Entier & e1,const Entier &
e1,const Entier & e2); e2)
#endif {
cout << "operator+(Entier,Entier)"<<endl;
return [Link]()+[Link]();
}
Gritli malek 126
Les changements de type
● Le programme ci-dessous met en évidence le rôle des constructeurs à 1 argument
pour la conversion de type.
#include<iostream>
#include "entier.h"
using namespace std;
void f(Entier e)
{
cout << "valeur de e = " << [Link]() << endl;
}
void main(void)
{
int varInt=4;
float varFloat=2.5;
f(varInt); //conversion int->Entier
f(varFloat); //conversion float->Entier
Entier e(3);
e=e+varFloat; // conversion float->Entier
e=1+e; // conversion int->Entier
}

Gritli malek 127


Les changements de type
● La règle suivante s’applique en C++ : lorsqu’un objet de type classe Entier est
attendu, et qu’une variable autre de type autreType est fournie, le compilateur
cherche si parmi les constructeurs de la classe Entier il en exite un qui accepte un
seul argument de type autreType . Si un tel constructeur exite, un objet temporaire
est construit avec ce constructeur et est utilisé à la place de la variable autre .
● Les constructeurs à un argument définissent une règle de conversion.l’opérateur +
attend un objet de type string , on lui passe une variable de type char * .Le
compilateur cherche donc s’il est possible de convertir la chaîne "ajout" en un objet
de type string . C'est possible car la classe string dispose d'un constructeur à un
argument de type char * . La chaîne "ajout" est donc concaténée ici à la chaine s1 .
Grâce à celà, on peut utiliser des chaînes du C à la place des objets string .

string s1("chaine"); // constructeur string::string(char *)


s1=s1+"ajout"; // conversion char * -> string avant appel de l'opérateur +

Gritli malek 128


Les changements de type
● On peut néanmoins mentionner dans la classe que les constructeurs ne soient
utilisés comme convertisseurs que de manière explicite (mot clé explicit ).

class Entier #include<iostream>


{ #include "entier.h"
public: using namespace std;
Entier(); void f(Entier e){
explicit Entier(int val); // utiliable seulement cout << "valeur de e = " << [Link]() << endl;
pour conversions explicites }
explicit Entier(float val); int main( ){
... int varInt=4;
}; float varFloat=2.5;
f((Entier)varInt);
f((Entier)varFloat);
Entier e(3);
e=e+(Entier)varFloat;
e=(Entier)1+e;
}

Gritli malek 129


Conversion d’un type classe vers un
type primitif
● On peut également définir des règles permettant de convertir un objet d’un type
classe en un type de base. Il s’agit en fait de définir le traitement réalisé par un
opérateur de cast
//complexe.h perator double(){ // opérateur de cast
#ifndef __COMPLEXE__ cout << "complexe::operator double() \t
#define __COMPLEXE__ obj:"<<this<<endl;
#include<iostream> return re; // la conversion complexe->double
class complexe retourne la partie réelle
{private: }
double re; // partie réelle friend ostream & operator<<(std::ostream &
double im; // partie imaginaire flot,const complexe & c);
public: }; // fin de la classe
complexe(double r=0,double i=0){ std::ostream & operator<<(std::ostream & flot,
cout << "complexe(double=0,double=0) \t const complexe & c)
objet : "<<this << endl; {
re=r;im=i;} flot << [Link];
complexe operator+(const complexe & c) if([Link]){flot << "+"<<[Link]<<"i";}
const return flot;
{ }
cout << "complexe::operator+() \t #endif
obj:"<<this<<" + obj:" <<&c<<endl;
Gritli malek 130
return complexe(re+[Link],im+[Link]);
}
Conversion d’un type classe vers un type
primitif

#include "complexe.h"
void main()
{
complexe c1,c2(1,3); //1)-2)
c1=2.4; //3)
cout << c1 << endl; //4)
c2=c1+c2; //5)-6)
cout << c2 << endl; //7)
c1=c1+(complexe)3.5; //8)-9)-10)
cout << c1 << endl; //11)
c1=(double)c1+3.5 //12)-13);
cout << c1 << endl; //14)
double d;
d=c1; //15)
cout << d << endl; //16)
// c1=c1+3.5; // << error c2666 :
2 overloads have similar conversions
}

Gritli malek 131


Bonne révision !!!

Gritli malek 132

Vous aimerez peut-être aussi