Vous êtes sur la page 1sur 26

1.

L ES CLASSES , LES OBJETS


1.1. Compilation spare. Lorsque lon programme seul ou plusieurs sur des projets importants, on doit sparer les tches en fonctions, classes, ....
En phase de dveloppement, ces fonctions et ces classes doivent tre rparties dans plusieurs
fichiers sur le modle suivant :
. Un fichier main.cxx dans lequel on teste les diffrents composants... et cest tout.
1
2
3
4
5
6
7

#include <stdlib.h>
#include util.h
int main()
{
int i=doubler(3);
double x = tripler(0.0);//etc...
}

. Un fichier util.h dans lequel on prototype (dclare) les diffrents composants :


1
2
3
4
5

#ifndef UTIL_H
#define UTIL_H
void doubler(int);
void tripler(float);
#endif

. Un fichier util.cxx dans lequel on implmente les composants.


1
2
3
4
5
6
7
8
9
10
11
12

#include util.h
//ici des utilitaires et
//fonctions utiliss par main()
void tripler(int y)
{
cout<<endl<<3*y;
y=3*y;
};
double doubler(float x)
{
x*=2;
};

. Compilation
$ g++ -c main.cxx util.cxx
. Edition des liens (link)
$ g++ main.o util.o -o mon_prog
. Raccourci : compilation + dition des liens
$ g++ main.cxx util.cxx -o mon_prog
1

Janvier 2010

C++

Pierre Puiseux

F IG . 1.1. Compilation spare


1.2. Les classes.
. Une classe est un type dfini par lutilisateur (au mme titre quun float ou un int). Une
classe se dclare laide du mot cl class et regroupe des attributs (ou attributs) et des
mthodes (ou mthodes) agissant sur ces attributs.
. Dclaration dune classe, exemple :
1
2
3
4
5
6
7
8

class Etudiant
{
private :
char nom_[20];
int age_;
float note_exam_,note_partiel_,note_finale_;
public : //methodes et donnees publiques
float rien;

9
10
11

//calcul note finale, implemente ailleurs


float calculerNoteFinale(void);

12
13

UPPA

//retourne la note finale implementation inline


2

LMA

Janvier 2010

15

UPPA

Pierre Puiseux

float noteFinale(void)
{return note_finale_;}

14

16

C++

};

LMA

Janvier 2010

C++

Pierre Puiseux

1.3. Les objets.


. Un objet est une instance dune classe, cest dire un emplacement mmoire adressable dont le
type est celui de la classe. De mme que int nest pas un objet mais un type, int i; dclare
un objet de type int, ou encore une instance dint.
. Exemple :
1
2
3
4

// declaration d'objets de la classe Etudiant


Etudiant a, b, c;
//donne la taille en octets de la classe
int size=sizeof (Etudiant)

. Pour les types prdfinis (int, float,...) on parle de type et de variables.


. Pour les types dfinis par lutilisateur, on parle de classe et dinstances (ou objets).
. On peut crer dynamiquement des instances de classe en faisant appel au ( un des) constructeur(s) de la classe (cf : 1.9). Le compilateur gnre un constructeur par dfaut.
1
2
3
4

UPPA

Etudiant *a;
a = new Etudiant();
...
delete() a;

LMA

Janvier 2010

C++

Pierre Puiseux

1.4. Allocation dynamique. Loprateur new() permet dallouer dynamiquement lespace mmoire ncessaire pour une (ou plusieurs) instance(s) de classe donne, de la mme manire que
pour les types standard :
. Etudiant* pe=new Etudiant; pour un seul exemplaire
. Etudiant* pe=new Etudiant[10]; pour un tableau de 10 tudiants.
La dsallocation se fait par loprateur delete ou delete[]
1.5. Accs aux membres. Soit e une instance de classe Etudiant dclare par
. Etudiant e;
1.5.1. Loprateur ".". Permet un accs direct aux attributs ou mthodes de linstance :
. cout <<e.nom; accs lattribut nom
. e.calculNoteFinale(); excution de la mthode calculNoteFinale
1.5.2. Loprateur "->". Permet laccs aux aux attributs ou mthodes de linstance via un pointeur. Par exemple, si un pe est un pointeur sur Etudiant,
. Etudiant* pe = &e;
alors on accde aux membres de e par pe->xxx qui quivaut e.xxx ou encore (*pe).xxx :
. cout <<pe->nom;
. pe->calculNoteFinale();

UPPA

LMA

Janvier 2010

C++

Pierre Puiseux

1.6. Oprateur de rsolution de porte.


. Une classe toto est dclare (ou prototype) dans fichier toto.h
. elle est dfinie (ou implmente) dans le fichier toto.cxx
dans le fichier toto.cxx, laccs aux membres de la classe toto se fait grce loprateur de rsolution de porte
Example.
. fichier toto.h
1
2
3
4
5

class Toto
{
private : float x_;
public : void faireQuelqueChose(void);
};

. fichier toto.cxx
1
2
3
4
5
6

UPPA

#include "toto.h"
void Toto::faireQuelqueChose(void)
{
cout << endl
<<"d'accord";
}

LMA

Janvier 2010

C++

Pierre Puiseux

1.7. Le pointeur this this. Permet un objet daccder ses propres membres.
Dans une mthode, il est frquent que lobjet lui-mme fasse un accs ses propres membres.
Dans ce cas on a une notation simplifie : on crit le membre tout seul, sans expliciter lobjet en
question. Cest ce que nous avons fait dans les fonctions de la classe Etudiant :
1
2
3
4
5
6
7
8
9
10

class Etudiant
{
...
void details() const
{
cout << nom_ <<";"<< age_
<< " ans ; Examen :"<< note_exam_
<< " partiel: " << note_partiel_;
}
...
};

Il arrive que dans une mthode il soit ncessaire de faire rfrence lobjet (tout entier) travers
lequel la mthode est appele. On dispose pour cela de la variable this qui est un pointeur vers
lobjet en question. Par exemple, la mthode Etudiant::details() peut scrire de manire
quivalente :
1
2
3
4
5
6
7
8
9
10

class Etudiant
{
...
void details() const
{
cout << this->nom_ <<";"<< this->age_
<< " ans ; Examen :"<< this->note_exam_
<< " partiel: " << this->note_partiel_;
}
...
};

Tout se passe comme si this tait pass la mthode comme un argument cach... ce qui est
effectivement le cas.
En ralit, les classes C++ sont des objets C dguis de la manire suivante : si a=A(); est
une instance de type A, et b(); est une mthode de A, alors un appel a.b(); est traduit par le
prcompilateur en un appel A::b(a);. Linstance a est largument fantme de la mthode A::b().
En C++ cet argument reste fantme tandis que dans dautres langages, il est explicitement pass
comme argument de la mthode. En Python , par exemple, une mthode b() de la classe A se
dclare
def b(self):...
o self est lquivalent Python du this de C++ .
Dans certaines bibliothques C++ , le pointeur lthis est explicitement indiqu, pour toute utilisation des mthodes et attributs de la classe, comme dans la classe Etudiant ci-dessus.
UPPA

LMA

Janvier 2010

C++

Pierre Puiseux

1.8. Membres publics et privs.


. les diffrents membres dune classe peuvent tre accs protg, priv ou public
private
les membres privs ne peuvent tre utiliss (modifis pour les donnes, appels pour les
mthodes) que par les mthodes de la classe elle-mme
protected
les membres protgs ne peuvent tre utiliss que par les mthodes de la classe et des classes
drives.
public
les membres publics peuvent tre utiliss et modifis par tous
. Les variables sont le plus souvent private ou protected, tandis que les mthodes sont
usuellement public.
. par convention le nom des membres private ou protected se termine par _. exemple :
nom_
. Soit a une variable de type Etudiant (voir 1.2).
Linstruction a.age_ = 21; produit une erreur de compilation car age_ est a accs
priv.
Linstruction a.rien = 0.0 est licite.
Example. Une classe Point :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

class Point {
public:
void afficher()
{cout << '(' << x_ << ',' << y_ << ')';}
void move(int a, int b)
{x_ = a; y_ = b;}
double distance(const Point& autrePoint)
{
int dx = x_ - autrePoint.x_;
int dy = y_ - autrePoint.y_;
return sqrt(dx*dx + dy*dy);
}
private:
int x_, y_;
};
Lors dun appel tel que p.distance(q) lobjet p accde aux membres privs x_ et y_ de lobjet
q. On dit que C++ pratique lencapsulation au niveau de la classe, non au niveau de lobjet.

UPPA

LMA

Janvier 2010

C++

Pierre Puiseux

1.9. Constructeurs. Un constructeur dune classe est une mthode spciale qui :
. a le mme nom que la classe,
. nindique pas de type de retour,
. ne contient pas dinstruction return.
Le rle dun constructeur est dinitialiser un objet, notamment en donnant des valeurs ses attributs.
Le constructeur na pas soccuper de trouver lespace pour lobjet ; il est appel (immdiatement) aprs que cet espace ait t obtenu, et cela quelle que soit la sorte dallocation qui a t faite :
statique, automatique ou dynamique, cela ne regarde pas le constructeur. Exemple :
1
2
3
4
5
6
7
8

class Point {
private:
int x_, y_;
public:
Point(int a, int b)//constructeur
{ x_ = a; y_ = b; }
... autres methodes ...
};

Un constructeur de la classe est toujours appel, explicitement (voir ci-dessous) ou implicitement,


lorsquun objet de cette classe est cr, et en particulier chaque fois quune variable ayant cette
classe pour type est dfinie.
Cest le couple dfinition de la variable + appel du constructeur qui constitue la ralisation en
C++ du concept cration dun objet . Lintrt pour le programmeur est vident : garantir que,
ds leur introduction dans un programme, tous les objets sont garnis et cohrents, cest--dire
viter les variables indfinies, au contenu incertain.
Une classe peut possder plusieurs constructeurs, qui doivent alors avoir des signatures diffrentes :
1
2
3
4
5
6
7
8
9
10
11
12

class Point {
private:
int x_, y_;
public: //3 constructeurs
Point(int a, int b)
{ x_ = a; y_ = b; }
Point(int a)
{ x_ = a; y_ = 0; }
Point(void)
{ x_ = y_ = 0; }
... autres methodes ...
};

Lemploi de paramtres avec des valeurs par dfaut permet de grouper des constructeurs. La classe
suivante possde les mmes constructeurs que la prcdente :

UPPA

LMA

Janvier 2010

1
2
3

C++

Pierre Puiseux

class Point {
private:
int x_, y_;

4
5
6
7
8
9

public:
Point(int a = 0, int b = 0)
{x_ = a; y_ = b; }
... autres methodes ...
};

Comme les autres mthodes, les constructeurs sont en gnral dclars (prototyps) dans la classe
et implments ailleurs. Ainsi, la classe prcdente pourrait scrire galement
. dans le fichier point2d.h :
1
2
3
4
5
6
7

class Point {
private:
int x_, y_;
public:
Point(int a = 0, int b = 0)
...
};

. et, ailleurs, probablement dans le fichier point2d.cxx :


1
2
3

UPPA

#include "point2d.h"
Point::Point(int a, int b)
{x_ = a; y_ = b; }

10

LMA

Janvier 2010

C++

Pierre Puiseux

1.10. Membres constants.


1.10.1. Attributs constants. Un attribut dune classe peut tre qualifie const. Il est alors obligatoire de linitialiser lors de la construction dun objet, et sa valeur ne pourra par la suite plus tre
modifie.
A titre dexemple le nom dun Etudiant, ne doit pas changer au cours de la vie de linstance.
Lattribut nom_ est donc const (en plus de private). Il doit tre initialis par le constructeur et
ne peut pas tre modifi.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

class Etudiant
{
private :
const string nom_;
float note_exam_,note_partiel_;
public :
Etudiant(nom, float NE, float NP):nom_(nom)
{note_exam_=NE;note_partiel=NP;}
float NoteFinale(void)
{return 0.5*(note_exam_+note_partiel_);
const float NoteExam(void)
{return note_exam_;}
float& NoteExam(void)
{return note_exam_;}
};

1.10.2. Mthodes constantes. Le mot const plac la fin de len-tte dune mthode indique que
ltat de lobjet travers lequel la fonction est appele nest pas chang du fait de lappel. Cest
une manire de dclarer quil sagit dune mthode de consultation de lobjet, non dune mthode
de modification :
1
2
3
4
5
6

class Point
{
...
void move(int a, int b);
// modifie l'objet
float distance(Point p) const; // ne modifie pas l'objet
...
};

A lintrieur dune mthode const dune classe C le pointeur this est de type const C *
const (pointeur constant vers un C constant) : lobjet point par this ne pourra pas tre modifi.
Cela permet au compilateur dautoriser certains accs qui, sans cela, auraient t interdits. Par
exemple, examinons la situation suivante :
1
2

UPPA

void unemthode(const Point a)


{Point b;...double d = a.distance(b);... }

11

LMA

C++

Janvier 2010

Pierre Puiseux

la qualification const de la mthode distance est indispensable pour que lexpression prcdente
soit accepte par le compilateur. Cest elle seule, en effet, qui garantit que le Point a, contraint
rester constant, ne sera pas modifi par lappel de la mthode distance().
De mme que pour les arguments dune fonction,
il est conseill de qualifier const toute mthode qui peut ltre.
1.10.3. Coexistence des mthodes constantes et non constantes. La qualification const dune mthode
fait partie de sa signature. Ainsi, on peut surcharger une mthode non constante par une mthode
constante ayant, part cela, le mme en-tte. La mthode non constante sera appele sur les objets
non constants, la mthode constante sur les objets constants.
On peut utiliser cette proprit pour crire des mthodes qui neffectuent pas le mme traitement ou qui ne rendent pas le mme type de rsultat lorsquelles sont appeles sur un objet constant et lorsquelles sont appeles sur un objet non constant. Exemple (se rappeler que le rsultat
renvoy par une mthode ne fait pas partie de sa signature) :
1
2
3
4
5
6
7
8
9

class Point {
int x, y;
public:
int X() const { return x; }
int Y() const { return y; }
int& X() { return x; }
int& Y() { return y; }
...
};

Avec la dclaration prcdente, les mthodes X et Y sont scurises : sur un objet constant elles
ne permettent que la consultation, sur un objet non constant elles permettent la consultation et la
modification :
1
2
3
4
5
6
7
8

UPPA

const Point a(2,


Point b(4,5);
int r;
...
r = a.X();
//
a.X() = r;
//
r = b.X();
//
b.X() = r;

3);

Oui
ERREUR ( a.X() rend une valeur)
Oui
// Oui ( b.X() rend une rfrence)

12

LMA

C++

Janvier 2010

Pierre Puiseux

1.11. Exercices.
Exercise 1. Algorithme dEuclide
Prototypez, implmentez et tester une fonction qui calcule le pgcd de deux entiers suivant lalgorithme dEuclide :
PGCD(a, b)
Si b=0
alors PGCD=a
Sinon
r = a%b //egal au reste de la division entire (modulo) de a par b
PGCD=PGCD(b, r)
Ce qui pourrait donner en C :
1
2
3
4

int PGCD(int a, int b)


{
return b ? PGCD(b, a%b):a;
}

Exercise 2. Une classe Fraction. On se propose de programmer une classe dont les instances
se comportent autant que possible comme les fractions de Q. Le numrateur et le dnominateur
doivent tre entiers.
(1) Dterminer la structure de donnes idoine.
(2) Constructeur principal :
(a) Ecrire le prototype (dans fraction.h) du constructeur qui doit permettre dinstancier
une Fraction par linstruction :
. Fraction x(1,2);
(b) Ecrire la dfinition du constructeur (dans le fichier fraction.cxx). On prvoira un
arrt du programme si le dnominateur est nul (linstruction assert(cond); stoppe
le programme si cond a une valeur boolenne fausse).
(c) Ecrire un programme principal, dans fraction.cxx, qui teste ce constructeur.
(3) Autres constructeurs :
(a) prototypez les constructeurs (fichier fraction.h) qui permettent dinstancier des
fractions ainsi :
1
2
3

Fraction x(1,2); // => 1/2


Fraction y(2); // numerateur seul => 2/1
Fraction t(x); // recopie => 1/2

(a) dfinissez ces constructeurs (fichier fraction.cxx)


(b) testez ces constructeurs
UPPA

13

LMA

Janvier 2010

C++

Pierre Puiseux

(4) Prototypez, puis dfinissez, puis testez les mthodes suivantes :


(a) une mthode denominateur() pour consulter le dnominateur,
(b) une mthode setDenominateur() pour modifier le dnominateur,
(c) une mthode reduire() qui ne renvoit rien mais rduit la fraction sa forme irrductible (on pourra utiliser la fonction PGCD crite ci-dessus). Testez par une instruction z.reduire();
(d) une mthode reduite() qui renvoit la fraction sous sa forme irrductible. Testez par
une instruction z.reduite();
(e) une mthode ecrire(ostream&) pour crire la fraction sur un flux. Testez la avec
linstruction Fraction z(1,2);z.ecrire(cout);
(5) Solution :09-classes/fraction0.h et 09-classes/fraction0.cxx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

1
2
3
4
5
6

#include <fstream>
#include <iostream>
using namespace std;
int PGCD(const int, const int);
class Fraction
{
public:
int n_,d_;
Fraction(int a=0, int b=1);
Fraction(const char*);
Fraction(const Fraction&);
void setNumerateur(const int& a);
int numerateur(void);
void reduire(void);
Fraction reduite(void);
void ecrire(ostream& out) ;
};
#include <fstream>
#include <iostream>
#include <iomanip>
#include <assert.h>
#include "fraction0.h"
using namespace std;

7
8
9

int PGCD(const int a, const int b)


{return b ? PGCD(b,a%b):a;}

10
11
12
13
14

UPPA

Fraction::Fraction(int a, int b):n_(a),d_(b)


{assert(b!=0);}
Fraction::Fraction(const Fraction& f):n_(f.n_),d_(f.d_)
{}
14

LMA

Janvier 2010
15
16
17
18

C++

Pierre Puiseux

void Fraction::setNumerateur(const int& a)


{n_=a;}
int Fraction::numerateur(void)
{return n_;}

19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51

void Fraction::reduire(void)
{
int pgcd = PGCD(n_, d_);
n_ /= pgcd;
d_ /= pgcd;
}
Fraction Fraction::reduite(void)
{
Fraction w(n_,d_);
w.reduire();
return w;
}
void Fraction::ecrire(ostream& out)
{
int n(n_), ln(0);
while (n!=0) {n/=10;ln++;}
int d(d_), ld(0);
while (d!=0) {d/=10;ld++;}
int l = max(ln,ld);
int m = (ln+ld)/2;
cout << setw(max(m,ln)) << n_ << endl;
if (d_==1) return;
for (int i=0; i<l;i++)
out<< "-";
out << endl << setw(max(m,ld)) << d_;
}
int main()
{
Fraction X(100,50); X.ecrire(cout);cout << endl;
X.reduire();
X.ecrire(cout);cout << endl;
Fraction Y(X.reduite());Y.ecrire(cout);cout << endl;
}

Exercise 3. La classe Fraction :


(1) Revisitez la classe Fraction et dclarez les attributs numrateur et dnominateur privs.
(2) Modifier soigneusement les dclarations des mthodes et de leurs arguments qui doivent
tre constants.
(3) Prototypez, puis dfinissez, puis testez les mthodes suivantes, en dcidant si elle doivent
tre constantes ou non :
UPPA

15

LMA

Janvier 2010

C++

Pierre Puiseux

(a) une mthode reel() qui renvoit la valeur relle sous forme dun double,
(b) une mthode mult(f) qui ne modifie pas la Fraction elle-mme, qui renvoit le
produit de la fraction elle-mme par la Fraction f.
(c) Une version fonction de la mthode mult qui permette dcrire Fraction f1(1,2)
,f2(3,4),f=mult(f1,f2);
(4) Solution :09-classes/fraction1.h et 09-classes/fraction1.cxx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

1
2
3
4
5
6
7
8

#include <fstream>
#include <iostream>
using namespace std;
class Fraction
{
private:
int n_,d_;
public:
Fraction(int a=0, int b=1);
Fraction(const char*);
Fraction(const Fraction&);
void setNumerateur(const int& a);
int numerateur(void) const;
void reduire(void);
Fraction reduite(void) const;
void ecrire(ostream& out) const;
double reel(void) const;
Fraction mult(const Fraction&) const;
};
Fraction mult(const Fraction&, const Fraction&);
int PGCD(const int, const int);
#include <fstream>
#include <iostream>
#include <iomanip>
#include <assert.h>
#include "fraction1.h"
using namespace std;
int PGCD(const int a, const int b)
{return b ? PGCD(b,a%b):a;}

9
10
11
12
13
14
15
16

UPPA

Fraction::Fraction(int a, int b):n_(a),d_(b)


{assert(b!=0);}
Fraction::Fraction(const Fraction& f):n_(f.n_),d_(f.d_)
{}
void Fraction::setNumerateur(const int& a)
{n_=a;}
int Fraction::numerateur(void) const
16

LMA

Janvier 2010
17
18
19
20
21
22
23

C++

Pierre Puiseux

{return n_;}
void Fraction::reduire(void)
{
int pgcd = PGCD(n_,d_);
n_ /= pgcd;
d_ /= pgcd;
}

24
25
26
27
28
29
30

Fraction Fraction::reduite(void) const


{
Fraction f(*this);
f.reduire();
return f;
}

31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62

UPPA

void Fraction::ecrire(ostream& out) const


{
int n(n_), ln(0);
while (n!=0) {n/=10;ln++;}
int d(d_), ld(0);
while (d!=0) {d/=10;ld++;}
int l = max(ln,ld);
int m = (ln+ld)/2;
cout << setw(max(m,ln)) << n_ << endl;
if (d_==1) return;
for (int i=0; i<l;i++)
out<< "-";
out << endl << setw(max(m,ld)) << d_;
}
//version methode
Fraction Fraction::mult(const Fraction& f) const
{return Fraction(n_*f.n_, d_*f.d_);}
//Version fonction
Fraction mult(const Fraction& z1, const Fraction& z2)
{return z1.mult(z2);}
int main()
{
Fraction X(100,50);
X.ecrire(cout);
X.reduire();
cout << endl;
X.ecrire(cout);
Fraction Y(50,3);
Fraction Z=X.mult(Y);
Z.reduire();
cout << endl;

17

LMA

Janvier 2010

Pierre Puiseux

Z.ecrire(cout);
f1(1,2),f2(3,4),W=mult(f1,f2);
W.ecrire(cout);cout << endl;
W.reduite().ecrire(cout);
cout << endl ;
W.ecrire(cout);

63
64
65
66
67
68
69

C++

Exercise 4. La classe Fraction. 1


(1) Dans la classe Fraction, dclarer, puis dfinir puis tester une mthode pour la lecture sur
un flux : on veut pouvoir crire par exemple Fraction z;z.lire(cin);
(2) Dclarer, puis dfinir puis tester une fonction permettant dadditionner deux fractions z1
et z2 ainsi : Fraction z=add(z1,z2);
(3) Utiliser la fonction add pour en dclarer, dfinir et tester une version mthode qui fonctionnera ainsi : Fraction z=z1.add(z2);
(4) Sur un modle analogue, imaginez une version fonction et une version mthode pour la
multiplication de deux fractions.
(5) Solution :09-classes/fraction2.h et 09-classes/fraction2.cxx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

#include <fstream>
#include <iostream>
using namespace std;
class Fraction
{
private:
int n_,d_;
public:
Fraction(int a=0, int b=1);
Fraction(const char*);
Fraction(const Fraction&);
void setNumerateur(const int& a);
int numerateur(void) const;
void reduire(void);
Fraction reduite(void) const;
void ecrire(ostream& out) const;
void lire(istream& in);
double reel(void) const;
Fraction mult(const Fraction&) const;
Fraction add(const Fraction&) const;
};
Fraction mult(const Fraction&, const Fraction&);
Fraction add(const Fraction&, const Fraction&);

1Au paragraphe suivant, nous amliorerons ces mthodes pour les rendre plus commode utiliser.

UPPA

18

LMA

Janvier 2010
24

1
2
3
4
5
6
7
8

C++

Pierre Puiseux

int PGCD(const int, const int);


#include <fstream>
#include <iostream>
#include <iomanip>
#include <assert.h>
#include "fraction2.h"
using namespace std;
int PGCD(const int a, const int b)
{return b ? PGCD(b,a%b):a;}

9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

Fraction::Fraction(int a, int b):n_(a),d_(b)


{assert(b!=0);}
Fraction::Fraction(const Fraction& f):n_(f.n_),d_(f.d_)
{}
void Fraction::setNumerateur(const int& a)
{n_=a;}
int Fraction::numerateur(void) const
{return n_;}
void Fraction::reduire(void)
{
int pgcd = PGCD(n_,d_);
n_ /= pgcd;
d_ /= pgcd;
}

24
25
26
27
28
29
30

Fraction Fraction::reduite(void) const


{
Fraction f(*this);
f.reduire();
return f;
}

31
32
33
34
35
36
37
38
39
40
41
42
43
44

UPPA

void Fraction::ecrire(ostream& out) const


{
int n(n_), ln(0);
while (n!=0) {n/=10;ln++;}
int d(d_), ld(0);
while (d!=0) {d/=10;ld++;}
int l = max(ln,ld);
int m = (ln+ld)/2;
out << setw(max(m,ln)) << n_ << endl;
if (d_==1) return;
for (int i=0; i<l;i++)
out<< "-";
out << endl << setw(max(m,ld)) << d_;
19

LMA

Janvier 2010
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59

C++

Pierre Puiseux

}
void Fraction::lire(istream& in)
{in >> n_ >> d_;}
//version methode
Fraction Fraction::mult(const Fraction& f) const
{return Fraction(n_*f.n_, d_*f.d_);}
//Version fonction
Fraction mult(const Fraction& z1, const Fraction& z2)
{return z1.mult(z2);}
//version methode
Fraction Fraction::add(const Fraction& f) const
{return Fraction(n_*f.d_+ d_*f.n_,d_*f.d_);}
//Version fonction
Fraction add(const Fraction& z1, const Fraction& z2)
{return z1.add(z2);}

60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90

UPPA

int main()
{
Fraction X(100,50);
X.ecrire(cout);
X.reduire();
cout << endl;
X.ecrire(cout);
Fraction Y(50,3);
Fraction Z=X.mult(Y);
Z.reduire();
cout << endl;
Z.ecrire(cout);
Fraction W=mult(X,Y);
cout << endl;
W.ecrire(cout);
cout << endl ;
W.reduite().ecrire(cout);
cout << endl ;
W.ecrire(cout);
cout <<endl<< PGCD(60,36);
ifstream fin ("fraction.txt");
W.lire(fin);
cout << endl;
W.ecrire(cout);
X=Fraction(2,3);
Y=Fraction(7,8);
W=add(X,Y);
cout << endl;
W.ecrire(cout);

20

LMA

Janvier 2010

C++

Pierre Puiseux

91
92

UPPA

21

LMA

C++

Janvier 2010

Pierre Puiseux

1.12. Surcharge des oprateurs internes. Une premire mthode pour surcharger les oprateurs
consiste les considrer comme des mthodes normales de la classe sur laquelle ils sappliquent.
Le nom de ces mthodes est donn par le mot-cl operator, suivi de loprateur surcharger. Le
type de la fonction de loprateur est le type du rsultat donn par lopration, et les paramtres,
donns entre parenthses, sont les oprandes.
Les oprateurs de ce type sont appels oprateurs internes, parce quils sont dclars lintrieur de la classe.
Voici la syntaxe :
type operatorOp(paramtres)
A + B est traduite par le compilateur par : A.operator+(B)
A * B est traduite par le compilateur par : A.operator*(B)
etc...
A Op B est traduite par le compilateur par : A.operator Op(B)
Avec cette syntaxe, le premier oprande est toujours lobjet auquel cette fonction sapplique.
Cette manire de surcharger les oprateurs est donc particulirement bien adapte pour les oprateurs qui modifient lobjet sur lequel ils travaillent, comme par exemple les oprateurs =, +=, ++,
etc. Les paramtres de la fonction oprateur sont alors le deuxime oprande. Les oprateurs dfinis en interne renvoient souvent lobjet sur lequel ils travaillent (ce nest pas une ncessit cependant). Cela est faisable grce au pointeur this, qui est un pointeur sur lobjet lui-mme. Voici
quelques oprateurs possibles pour la classe Point2D :
1
2
3
4
5
6
7
8
9
10

UPPA

Point2D & operator+=(const Point2D & p)


{x=x+p.x;y=y+p.y; return *this;}
Point2D & operator-=(const Point2D & p)
{x=x-p.x; y=y-p.y; return *this;}
Point2D & operator*=(double v)
{x=x*v; y= y*v; return *this;}
double operator*(const Point2D & p)
{return x*p.x+y*p.y;}
Point2D & operator=(const Point2D & p)
{x = p.x; y=p.y; return *this; }

22

LMA

Janvier 2010

C++

Pierre Puiseux

1.13. Surcharge des oprateurs externes. Une deuxime possibilit nous est offerte par le langage
pour surcharger les oprateurs. La dfinition de loprateur ne se fait plus dans la classe qui lutilise, mais en dehors de celle-ci, par surcharge dun oprateur de lespace de nommage global. Il
sagit donc doprateurs externes cette fois. Dans ce cas, tous les oprandes de loprateur devront
tre passs en paramtres : il ny aura pas de paramtre implicite.
La syntaxe est la suivante :
type operatorOp(oprandes)
o oprandes est la liste complte des oprandes.
Lavantage de cette syntaxe est que loprateur est rellement symtrique, contrairement ce
qui se passe pour les oprateurs dfinis lintrieur de la classe. Les oprateurs externes doivent
tre dclars comme tant des fonctions amies (friend) de la classe sur laquelle ils travaillent, faute
de quoi ils ne pourraient pas manipuler les attributs de leurs oprandes. Les oprateurs de lecture
ou dcriture sont dfinis comme oprateurs externes. En guise dexemple on rajoute dans la classe
Point2D un oprateur dcriture :
Fichier Point2D.h :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

27

UPPA

class Point2D {
double x,y;
public:
Point2D(){}
Point2D(double a, double b)
{x=a; y=b;}
~Point2D(){}
double Distance() const
double Distance(const Point2D& p) const
double & GetX(){return x;}
const double & GetX() const
{return x;}
double & GetY(){return x;}
const double & GetY() const
{return x;}
Point2D & operator+=(const Point2D & p)
{x=x+p.x;y=y+p.y; return *this;}
Point2D & operator-=(const Point2D & p)
{x=x-p.x; y=y-p.y; return *this;}
Point2D & operator*=(double v)
{x=x*v; y= y*v; return *this;}
double operator*(const Point2D & p)
{return x*p.x+y*p.y;}
Point2D & operator=(const Point2D & p)
{ x = p.x; y=p.y; return *this; }
friend ostream & operator << (ostream & os, const Point2D
& p);
};

23

LMA

Janvier 2010

C++

Pierre Puiseux

Fichier Point2D.Cxx :
1
2
3
4
5
6
7
8
9
10
11
12
13
14

#include "Point.h"
#include <math.h>
/*--------------------------------------*/
double Point2D::Distance() const
{return sqrt(x*x+y*y));}
/*--------------------------------------*/
double Point2D::Distance (const Point2D & p) const
{return sqrt(pow(p.x-x,2.)+pow(p.y-y,2.));}
/*---------------------------------------*/
ostream & operator << (ostream & os, const Point2D & p)
{
os <<"(x,y)=("<<x<<","<<y<<")";
return os;
}

Ici loprateur dcriture renvoie la rfrence de lobjet os de manire pouvoir, enchainer les
commandes dcriture. On peut donc crire
cout<<p1<<p2;
Si p1 et p2 sont deux objets de type Point2D.

UPPA

24

LMA

Janvier 2010

C++

Pierre Puiseux

1.14. Fonctions et classes amies (friend). Extrait de


http ://casteyde.christian.free.fr/cpp/cours/online/x3010.html
Il est parfois ncessaire davoir des fonctions qui ont un accs illimit aux champs dune classe.
De telles fonctions sont appeles des fonctions amies. Pour quune fonction soit amie dune classe,
il faut quelle soit dclare dans la classe avec le mot cl friend.
Il est galement possible de faire une classe amie dune autre classe, mais dans ce cas, cette
classe devrait peut-tre tre une classe fille. Lutilisation des classes amies peut traduire un dfaut
de conception.
Les fonctions amies ne sont pas des mthodes de la classe (cela naurait pas de sens puisque les
mthodes ont dj accs aux membres de la classe).
Example 5. Fonctions amies
1
2
3
4
5
6
7
8
9
10
11

class A
{
int a; // Une donnee privee.
friend void ecrit_a(int i); // Une fonction amie.
};
A essai;
void ecrit_a(int i)
{
essai.a=i; // Initialise a.
return;
}
1.15. Exercices.
Exercise 6. Classe Fraction : surcharges doprateurs. On partira de la version fraction2.cxx et
fraction2.h
(1) Surchargez loprateur + comme oprateur-mthode de la classe (Pour viter de rcrire tout
loprateur, on rutilisera la fonction add ou la mthode Fraction::add dj dfinis.
(2) Surcharger loprateur * comme oprateur externe la classe (mme remarque concernant
la rutilisation des fonctions et de mthodes dja crites.)
(3) Ecrire les deux oprateurs dinsertion et dextraction de la classe Fraction. Testez ensuite
les instructions :
1
2
3

Fraction z(1,2),z1(2,3);
cout << z << endl << z1;
cin >> z;

(4) Corrig : voir dans fraction3.cxx et fraction3.h.


Exercise 7. Classe Fraction : on pourra partir de fraction3.cxx et fraction3.h que lon nettoiera
de ses mthodes et fonctions inutiles.
(1) Faire en sorte que les fractions manipules soit toujours rduites (pour cela, modifier les
constructeurs et les oprateurs idoines)
UPPA

25

LMA

Janvier 2010

C++

Pierre Puiseux

(2) Ecrire une mthode Fraction::inverser(...) qui inverse in-situ une fraction.
(3) Ecrire une fonction inverse(...) qui renvoit linverse dune fraction, sans modifier la
fraction elle-mme.
(4) Un oprateur-fonction unaire : operator-(...) qui renvoit loppos dune fraction, sans
modifier la fraction elle-mme.
(5) Un oprateur-fonction binaire operator-(...) qui calcule et renvoit la diffrence de
deux fractions sans les modifier.
(6) Un oprateur-mthode operator+=(...) qui permette dcrire Fraction f(1,2),f1
(2,3);f+=f1;. Pour cet oprateur, on ne cherchera pas utiliser les fonctions ou mthodes dj crites.
(7) Rcrire loprateur-fonction operator+(...) (et la fonction add(...)) en utilisant
loprateur-mthode +=.
(8) Corrig : voir fraction4.cxx et fraction4.h.

UPPA

26

LMA