Classes 1
Classes 1
Classes 1
1
La notion d instance ou d objet
• Une classe étant définie, on peut construire des instances ou objets
de cette classe
» p1 et p2 sont des instances ou objets de la classe « Point »
Point p1, p2;
p1.m_x = 5; p2.m_x = 10; // Impossible car m_x et m_y privees
p2 = p1; // Autorisé
2
Les fonctions membres d une classe
• Exemple de définition
void Point::initialise(int abs, int ord) {
// abs est affecté au champ m_x de l’objet appelant
m_x = abs;
m_y = ord;
}
void Point::deplace(int dx, int dy) {
m_x = m_x + dx;
m_y = m_y + dy;
}
void Point::affiche() {
cout << “(” << m_x “,” << m_y << “)”;
}
» Les fonctions membres d une classe peuvent donc accéder aux membres
privés d une classe
3
Les fonctions membres d une classe
• Différents types de fonctions membres
» Les constructeurs permettent d initialiser automatiquement les objets
d une classe lors de leur déclaration
– Un constructeur est une fonction membre de même nom que la classe et qui n admet
pas de type de retour
– Une classe possède au moins un constructeur
– Une classe peut posséder plusieurs constructeurs (de prototypes différents)
» Les destructeurs permettent de détruire automatiquement les objets
à la fin de leur vie
– Un destructeur est une fonction membre de nom, le nom de la classe précédé du
caractère ~ et qui n admet pas de type de retour
– Une classe possède au plus un destructeur
» Les accesseurs et manipulateurs permettent respectivement
– d accéder aux informations privées contenues dans les objets
– de modifier les informations privées contenues dans les objets
4
Un premier exemple complet
• La définition d une classe « Point »
» Elle se fera par exemple dans un fichier d en-tête « Point.h »
class Point {
int m_x;
int m_y;
public:
Point(); // Constructeur par défaut
Point(int abs, int ord); // Deuxième constructeur
~Point(); // Destruteur de la classe
// Accesseurs ne pouvant modifier l’objet appelant
int abscisse() const;
int ordonnee() const;
// Manipulateur modifiant l’objet appelant
void deplace(int h, int v);
};
» Ne pas oublier le point-virgule a la fin de la définition d’une classe
5
Un premier exemple complet
• Définitions des fonctions membres de la classe « Point »
» Elles se feront par exemple dans un fichier source « Point.cpp »
Point::Point() {
cout << Appel du constructeur par défaut : ;
cout << this << endl;
m_x = 0; m_y = 0;
}
» Dans une fonction membre « this » est un pointeur de type « void* » contenant
l adresse de l objet appelant
Point::Point(int abs, int ord) {
cout << Appel du constructeur II pour l objet ;
cout << this << endl;
m_x = abs; m_y = ord;
}
6
Un premier exemple complet
• Définitions des fonctions membres de la classe « Point »
» Suite des fonctions membres
Point::~Point() {
cout << Appel du destructeur pour l objet ;
cout << this << endl;
}
int Point::abscisse()const {
return(m_x);
}
int Point::ordonnee() const {
return(m_y);
}
7
Un premier exemple complet
• Utilisation de la classe « Point »
#include <iostream.h>
#include “point.h”
void affiche(const Point& p) { // Passage par référence
cout << “(” << p.abscisse() << “,” << p.ordonnee() << “)”;
}
void main() {
Point p1; // Le constructeur par défaut est appelé
Point p2(2,4); // Le constructeur II est appelé
p1.deplace(6,2);
affiche(p1); cout << “ et ”;
affiche(p2); cout << endl;
}
Appel du constructeur par défaut pour l objet 0x10000
Appel du constructeur II pour l objet 0x20000
(6,2) et (2,4)
Appel du destructeur pour l objet 0x20000
Appel du destructeur pour l objet 0x10000
8
Exemple
• Exercice : définir un type « Vecteur » permettant de manipuler des vecteurs de
dimension trois et de composantes réelles.
TYPE vecteur
opérations
init : double x double x double → Vecteur
norme : Vecteur → double
produit : Vecteur x Vecteur → double
somme : Vecteur x Vecteur → Vecteur
afficher : Vecteur →
• le fichier d en-tête C++ « Vecteur.h » permettra de déclarer le type « Vecteur »
class Vecteur {
private :
double m_x;
double m_y;
double m_z;
public :
Vecteur(double c1 = 0, double c2 = 0, double c3 = 0);
double norme () const;
double produit (const Vecteur &v2) const;
Vecteur somme (const Vecteur &v2) const;
void afficher () const;
};
9
Exemple
• Vecteur.cpp
#include <iostream.h>
#include <math.h>
#include Vecteur.h
Vecteur::Vecteur(double c1, double c2, double c3) {
cout << "\nAppel du constructeur";
m_x = c1;
m_y = c2;
m_z = c3;
}
void Vecteur::afficher() const {
cout << "<";
cout << m_x << ", ";
cout << m_y << ", ";
cout << m_z << ">";
}
double Vecteur::norme() const {
double n;
n = m_x * m_x + m_y * m_y + m_z * m_z;
return(sqrt(n));
}
double Vecteur::produit(const Vecteur &v2) const {
return( m_x * v2.m_x + m_y * v2.m_y + m_z * v2.m_z);
} 10
Exemple
Vecteur Vecteur::somme(Vecteur &v2) const {
Vecteur v;
v.m_x = m_x + v2.m_x;
v.m_y = m_y + v2.m_y;
v.m_z = m_z + v2.m_z;
return(v);
}
• Utilisation de la classe
#include <iostream.h>
#include Vecteur.h
void main()
{
// Définition d objets ou d instanciations
// v1, v2 et v de la classe Vecteur
Vecteur v1(1.0, 3.0, 2.0), v2, v;
v2 = v1 ; // Copie champ à champ
// Définition d un pointeur (non initialisé)
Vecteur *pv;
pv = new Vecteur(-1.0,2.0,5.0);
// Impossible d écrire des instructions du type
// de v1.m_x = -1.0; ou pv->m_x = -1.0; car m_x privée
11
Exemple
cout << "\nv1 = "; v1.afficher();
cout << "\nv2 = "; v2.afficher();
cout << "\n*pv = "; pv->afficher();
cout << "\n|v1| = " << v1.norme();
cout << "\nv1.v2 = " << v1.produit(v2);
cout << "\nv1.(*pv) = " << v1.produit(*pv);
v = v1.somme(v2);
cout << "\nv1+v2 = " ; v.afficher();
v = pv->somme(v2);
cout << "\n(*pv)+v2 = "; v.afficher();
delete pv;
}
Affichage après exécution :
Appel du constructeur // Pour v1
Appel du constructeur // Pour v2
Appel du constructeur // Pour v
Appel du constructeur // Pour pv = new ...
v1 = <1.0, 3.0, 2.0>
v2 = <1.0, 3.0, 2.0> v1.(*pv) = 15
*pv = <-1.0, 2.0, 5.0> v1+v2 = <2.0, 6.0, 4.0>
|v1| = 3.74 v1+(*pv) = <0.0, 5.0, 7.0>
v1.v2 = 14
12
Encapsulation
• Les données membres m_x, m_y et m_z ne sont pas accessibles à un utilisateur de la
classe. Celui-ci ne peut que définir des vecteurs et les manipuler via les fonctions
membres publiques. On dit qu il y a encapsulation des données membres.
• Il serait possible de modifier la représentation des vecteurs sans que cela change le
mode d utilisation de la classe « Vecteur ».
class Vecteur {
private :
double m_x[3];
public :
Vecteur(double c1 = 0, double c2 = 0, double c3 = 0) {
m_x[0] = c1 ; m_x[1] = c2 ; m_x[2] = c3 ;
}
double norme() const {
double n;
n = m_x[0]*m_x[0]+m_x[1]*m_x[1]+m_x[2]*m_x[2]);
return(sqrt(n));
}
...
};
13
Fonctions membre en ligne
• C++ permet de définir des fonctions en ligne ⇒ efficacité du code
• Pour rendre en ligne une fonction membre, on peut :
» soit fournir directement la définition de la fonction dans la déclaration
même de la classe; dans ce cas, inline n est pas utilisé,
» soit procéder comme pour une fonction ordinaire en fournissant une
définition en dehors de la déclaration de la classe; dans ce cas, inline doit
apparaître, à la fois devant la déclaration et devant l en-tête.
14
La sur-définition d opérateurs
• Extension de la classe « Point »
» Ajouter une fonction membre permettant d ajouter un point à un autre
Point p1(2,4), p2(5,6), p = p1.ajouter(p2);
Point Point::ajouter(const Point& p2) {
Point s(m_x + p2.m_x, m_y + p2.m_y);
return(s);
}
» Comment simplifier l écriture du code précédent ?
Point p1(2,4), p2(5,6), p = p1 + p2;
Point Point::operator+(const Point& p2) {
Point s(m_x + p2.m_x, m_y + p2.m_y);
return(s);
}
p = p1 + p2; ⇔ p = p1.operator+(p2);
15
La sur-définition d opérateurs
• Exercice : Reprendre la classe « Vecteur » en surchargeant les opérateurs
d addition et de multiplication
#include <iostream.h>
#include Vecteur.h
void main()
{
Vecteur v1(1.0, 3.0, 2.0), v2(-1.0, 4.0), v;
cout << "\nv1 = ";
v1.afficher();
cout << "\nv2 = ";
v2.afficher();
cout << "\n|v1| = " << v1.norme();
cout << "\n|v2| = " << v2.norme();
//v = v1 * v2;
cout << "\nv1.v2 = " << v1 * v2;
//v.afficher();
v = v1 + v2;
cout << "\nv1+v2 = ";
v.afficher();
}
16
La sur-définition d opérateurs
class Vecteur {
private :
double m_x;
double m_y;
double m_z;
public :
// Constructeur automatiquement en ligne
// (car défini dans la classe)
Vecteur(double c1 = 0, double c2 = 0, double c3 = 0) {
m_x = c1 ; m_y = c2 ; m_z = c3 ; };
inline double operator* (Vecteur &v2);
inline Vecteur operator+ (Vecteur &v2);
};
inline double Vecteur::operator*(Vecteur &v2)
{
return( m_x * v2.m_x + m_y * v2.m_y + m_z * v2.m_z);
}
inline Vecteur Vecteur::operator+(Vecteur &v2){
Vecteur v;
v.m_x = m_x + v2.m_x;
v.m_y = m_y + v2.m_y;
v.m_z = m_z + v2.m_z;
return(v);
}
17
Fonctions amies
• Une fonction amie d une classe A est une fonction classique, qui sans être membre
de la classe, peut accéder directement à ses données ou fonctions membres, qu elles
soient privées ou publiques. Pour indiquer qu une fonction est amie d une classe, il
suffit de la déclarer dans cette classe en faisant précéder son en-tête du mot clef
« friend »
class Point {
private :
…
public :
…
// Déclaration de la fonction amie
friend void afficher(const Point &); // Exple de fonction indépendante amie d une classe
};
// Définition de la fonction amie
void afficher(const Point & p) {
// Accès direct aux données privées
cout << “(” << p.m_x << “,” << p.m_y << “)”;
}
18
Fonctions amies
• Autre exemple de fonction indépendante amie d une classe
#include <iostream.h>
class Point {
int x, y;
public :
Point(int abs=0, int ord = 0) { // un constructeur inline
x = abs; y = ord; }
// déclaration fonction amie (indépendante) nommée coincide
friend int coincide(Point, Point);
};
int coincide(Point p, Point q) { // définition de coincide
if (( p.x = = q.x) && (p.y = = q.y)) return 1;
else return 0;
}
19
Fonctions amies
main( ) {
Point a(1, 0), b(1), c;
if (coincide(a, b)) cout << “a coincide avec b \n“;
else cout << “a et b sont différents \n“;
if (coincide(a, c)) cout << “a coincide avec c \n“;
else cout << “a et c sont différents \n“;
}
21
La sur-définition d opérateurs
• Extension de la classe « Point »
» Sur-définir l opérateur « << » pour insérer des points dans des flots de sortie
// Fonction membre de la classe « ostream » : impossible
cout << p; ⇔ cout.operator<<(p);
// Fonction amie de la classe « Point »
cout << p; ⇔ operator<<(cout,p);
» Une fonction amie d’une classe peut accéder à ses membres privés
class Point {
. . .
friend void operator<<(ostream& sortie, const Point& p);
. . .
};
void operator<<(ostream& sortie, const Point& p) {
sortie << “(” << p.m_x “,” << p.m_y << “)”;
22
La sur-définition d opérateurs
• Extension de la classe « Point »
» Sur-définir l opérateur « << » pour insérer des points dans des flots de sortie
» cout << p1 << p2 ? ⇔ operator<<(operator<<(cout,p1), p2) ?
» Pour que cette expression ait un sens, il faut que :
– la sur-définition de l opérateur « << » retourne le canal de sortie passé en paramètre
– le retour se fasse par référence.
class Point {
...
friend ostream& operator<<(ostream& sortie, const Point& p);
...
};
ostream& operator<<(ostream& sortie, const Point& p) {
sortie << “(” << p.m_x “,” << p.m_y << “)”;
return(sortie);
}
Toute sur-définition de l opérateur « << » se présentera sur le même modèle.
Exercice : Sur-définir l opérateur « << » de telle sorte qu on ait une instruction de la
forme « cout << v », où v est un objet de la classe « Vecteur »
23
La notion de constructeur de copie
• Notion fondamentale pour toutes les classes possédant des données membres de types
pointeurs. Forme générale : Exemple(Exemple &); ou Exemple(const Exemple&);
• Situations d invocation
» Lors de la construction d un nouvel objet à partir d un objet existant
class Exemple {};
Exemple X; // Invocation du constructeur par défaut
Exemple Y(X); // Invocation du constructeur de copie
Exemple Y = X; // Invocation du constructeur de copie
» Lors du passage d un objet par valeur à une fonction
void f(Exemple A);
Exemple X; // Invocation du constructeur par défaut
f(X); // Invocation du constructeur de copie
// pour construire l’argument A à parir de X
» Lorsqu une fonction retourne un objet d une classe
Exemple g() {
Exemple A; // Invocation du constructeur par défaut
...
return(A); // Invocation du constructeur de copie pour
} // construire un objet temporaire à partir
// de A et retourner cet objet temporaire
24
La notion de constructeur de copie
• Exemple de situation où un constructeur de copie est nécessaire
» Définition d une classe permettant de manipuler des vecteurs de dimensions quelconques
class Vecteur {
int m_dimension;
float *m_x;
public:
Vecteur(int d) {
m_dimension = d; m_x = new float[d];
for(int i = 0; i < d; i++) m_x[i] = 0;
};
~Vecteur() { delete [] m_x; };
int dimension() { return(m_dimension); };
int composante(int i) { return(m_x[i]); };
void composante(int i, float x) { m_x[i] = x; };
};
void afficher(Vecteur w) {
for(int i = 0; i < w.dimension(); i++)
cout << w.composante(i) << “ “;
}
25
La notion de constructeur de copie
• Exemple de situation où un constructeur de copie est nécessaire
» Si un constructeur de copie n est pas défini par l utilisateur, alors le compilateur en
construit un automatiquement.
» Ce dernier effectue une copie champ à champ des données membres
Vecteur V(3); V 3 0 0 0
Afficher(V); V 3 0 0 0
W 3
26
La notion de constructeur de copie
• Exemple de définition d un constructeur de copie
Vecteur::Vecteur(const Vecteur& v) {
m_dimension = v.m_dimension;
m_x = new float[m_dimension];
for(int i = 0; i < m_dimension; i++)
m_x[i] = v.m_x[i];
};
Vecteur V(3); V 3 0 0 0
V.composante(1,7); V 3 0 7 0
Afficher(V); W 3 0 7 0
27
Sur-définition de l opérateur « = »
• Exemple de situation où une sur-définition est nécessaire
Vecteur V(3), W(2); V 3 0 0 0
W 2 0 0
28
Sur-définition de l opérateur « = »
• Exemple de définition – Première Version
void Vecteur::operator=(const Vecteur& v) {
delete [] m_x;
m_dimension = v.m_dimension;
m_x = new float[m_dimension];
for(int i = 0; i < m_dimension; i++)
m_x[i] = v.m_x[i];
}
29
Sur-définition de l opérateur « = »
• Exemple de définition – Version définitive
Vecteur& Vecteur::operator=(const Vecteur& v) {
if (this == &v) return(*this);
delete [] m_x;
m_dimension = v.m_dimension;
m_x = new float[m_dimension];
for(int i = 0; i < m_dimension; i++)
m_x[i] = v.m_x[i];
return(*this);
}
30
Définition canonique d une classe
• Définition minimale d une classe
class Exemple {
// Données membres privées
. . .
public:
// Fonctions membres publiques
Exemple(); // Constructeur par défaut
~Exemple(); // Destructeur
Exemple(Exemple&); // Constructeur de copie
Exemple& operator=(Exemple&); // Sur-définition de “=“
};
Exemple::Exemple() { . . . }
Exemple::~Exemple() { . . . }
Exemple::Exemple(Exemple& e) { . . . }
Exemple& Exemple::operator=(Exemple&) { . . . }
31
Exercice
• On considère la classe « Vecteur » suivante définie dans « Vecteur.h » :
class Vecteur {
int m_dimension;
double *m_x;
public:
Vecteur(int n); // Constructeur par défaut
Vecteur(const Vecteur &); // Constructeur de copie
~Vecteur(); // Destructeur
friend ostream& operator << (ostream&, Vecteur&);
double norme();
double operator * (Vecteur& v2);
Vecteur& operator = (Vecteur& v2);
Vecteur operator + (Vecteur& v2);
double& operator [] (int i); // ième composante du vecteur
};
Ecrire dans le fichier « Vecteur.cpp » les définitions des
fonctions et la fonction main.
32
Exercice
Vecteur::Vecteur(int n)
{
int i;
cout << "\nAppel du constructeur";
cout << " (adr = " << this << ")";
m_dimension = n;
m_x = new double[n];
for(i = 0; i < n; i++) m_x[i] = 0.0;
}
Vecteur::~Vecteur()
{
cout << "\nAppel du destructeur";
cout << " (adr = " << this << ")";
if (m_x) delete [] m_x;
}
double Vecteur::norme() {
double n = 0;
int i;
for(i = 0; i < m_dimension; i++) n += m_x[i] * m_x[i];
return(sqrt(n));
}
33
Exercice
ostream& operator << (ostream& sortie, Vecteur &v) {
int i;
for(i = 0; i < v.m_dimension; i++) {
if (i == 0) sortie << "< "; else sortie << ", ";
sortie << v.m_x[i];
}
sortie << " >";
return(sortie);
}
double Vecteur::operator * (Vecteur& v2) {
double p = 0;
int i;
if (this->m_dimension == v2.m_dimension) {
for(i = 0; i < this->m_dimension; i++)
p += this->m_x[i] * v2.m_x[i];
return(p);
} else {
cout << "\nVecteur de dimensions differentes !";
return(0);
}
}
34
Exercice
Vecteur Vecteur::operator + (Vecteur& v2) {
int min, i;
if (this->m_dimension != v2.m_dimension) {
cout << "\nVecteurs de dimensions differentes ! » ;
if (this->m_dimension > v2.m_dimension)
min = v2.m_dimension;
else
min = this->m_dimension;
} else
min = this->m_dimension;
Vecteur v(min);
for(i = 0; i < min; i++)
v.m_x[i] = this->m_x[i] + v2.m_x[i];
return(v);
}
double& Vecteur::operator [] (int i) {
if ((0 > i) || (i >= m_dimension))
cout << "\nAttention : mauvais indice";
else
return(m_x[i]);
}
bon exemple de l utilité du retour par référence 35
Exercice
void main() {
Vecteur v1(3);
Vecteur v2(2);
v1[0] = 1.0; // Appel à l opérateur sur-défini []
v1[1] = 3.0;
v1[2] = 2.0;
v2[0] = -1.0;
v2[1] = 4.0;
cout << "\nv1 = " << v1;
cout << "\nv2 = " << v2;
cout << "\n|v1| = " << v1.norme();
cout << "\n|v2| = " << v2.norme();
cout << "\nv1.v2 = " << v1 * v2;
cout << "\nv1 + v2 = " << v1 + v2;
}
A son exécution, le programme précédent afficherait les messages suivants :
Appel du constructeur (adr = 0x10000) // v1
Appel du constructeur (adr = 0x20000) // v2
v1 = <1.0,3.0,2.0>
v2 = <-1.0,4.0> v1+v2 = <0.0,7.0>
|v1| = 3.74 Appel du destructeur (adr = 0x20000) // v2
|v2| = 4.12 Appel du destructeur (adr = 0x10000) // v1
v1.v2 = 0 36
Les patrons de classes
» Les définitions de patrons doivent être placées dans des fichiers d en-tête
37
Les patrons de classes
• Autres exemples de patrons de fonctions
» Utilisation des paramètres de type dans le corps des patrons
template <class T> T* vect(T x, int n) {
T* pT = new T[n];
for(int i = 0; i < n; i++)
pT[i] = x;
return(pT);
}
float x = 0.5, *pF = vect(x,4);
int i = 4, *pI = vect(i,10);
38
Les patrons de classes
• Classes génériques ou modèles de classes
template <class T> class Point {
T m_x;
T m_y;
public:
Point(T x, T y) { m_x = x; m_y = y; };
void afficher();
};
template <class T> void Point<T>::afficher() {
cout << ( << m_x;
cout << , << m_y << ) ;
}
39
Les patrons de classes
• Classes génériques ou modèles de classes
» Autre exemple de patron avec paramètre de type et paramètre d expression
template <class T, int n> class Tableau {
T m_x[n];
public:
Tableau() { cout << constructeur par défaut ; };
T& operator[](int i) { return(m_x[i]); }
void afficher();
};
template <class T, int n> void Tableau<T,n>::afficher() {
for(int i = 0; i < n; i++)
cout << m_x[i];
}
Tableau<int,4> ti; ti[0] = 6;
Tableau<char,10> tc; tc[9] = e ;
Tableau<Point,5> tp; tp[1].deplacer(1,2);
40
Les membres statiques
• Rôle des données membres statique
» Permet de gérer des données communes à tous les objets ou instances d une classe
class Exemple {
static int m_compteur; // Nombre d objets créés
public:
Exemple() { m_compteur++; };
~Exemple() { m_compteur--; };
static void affiche() { cout << m_compteur; };
};
» Une fonction membre statique ne peut accéder qu aux autres membres statiques
» Une donnée membre statique ne peut être initialisée à l intérieur d un constructeur
int Exemple::m_compteur = 0;
void main() {
Exemple a ;
a.affiche(); // Affiche 1
Exemple *pB = new Exemple;
pB->affiche(); // Affiche 2
delete pB;
Exemple c;
c.affiche(); // Affiche 2
}
41