SlideShare a Scribd company logo
1 of 142
Download to read offline
Aziz DAROUICHI
1
Programmation Orientée Objet en C++:
Classes et Objets
2
Notion de classe
Portée des attributs
Définition des méthodes, Définition externe des méthodes
Actions et Prédicats
Déclaration et utilisation d’objets
Appel aux méthodes
Accès aux attributs et méthodes
Encapsulation et interface
Accesseurs et manipulateurs
Masquage/Shadowing
Pointeur sur l’objet courant : this
Constructeur, Liste d’initialisation, Chainage des constructeurs
Constructeur de copie, Destructeur
Création des instances dynamiques
Membres de classe
Typologie des méthodes d’une classe
Q & A
Notion de classe
Portée des attributs
Définition des méthodes, Définition externe des méthodes
Actions et Prédicats
Déclaration et utilisation d’objets
Appel aux méthodes
Accès aux attributs et méthodes
Encapsulation et interface
Accesseurs et manipulateurs
Masquage/Shadowing
Pointeur sur l’objet courant : this
Constructeur, Liste d’initialisation, Chainage des constructeurs
Constructeur de copie, Destructeur
Création des instances dynamiques
Membres de classe
Typologie des méthodes d’une classe
Q & A
Chapitre 5: Classes et Objets
2
3
Notion de classe
3
4
Notion de classe
4
5
Notion de classe
5
6
Définition d’une classe
// N'oubliez pas le point-virgule à la fin !
6
7
Définition d’une classe
Exemple 1:
class Rectangle
{
double hauteur;
double largeur;
double surface(){
return hauteur*largeur;
}
double perimetre() {
return 2*(hauteur+largeur);
}
};
Nom de la classe
Attributs/champs (fields)
Méthodes
7
8
Définition d’une classe
Exemple 2:
…
class Point
{
double x;
double y;
void translater(double dx, double dy){
x += dx;
y += dy;
}
double distance() {
return sqrt(x*x+y*y);
}
};
Attributs/champs (fields)
Nom de la classe
Méthodes
8
9
Déclaration des attributs
9
10
Attributs : c'est le nom que l'on donne aux variables contenues
dans des classes.
Exemple 1:
Les attributs hauteur et largeur, de type double, de la classe
Rectangle pourront être déclarés par :
class Rectangle {
double hauteur;
double largeur;
};
Déclaration des attributs
10
11
Exemple 2:
Nous supposerons ici qu’un objet de type Point sera représenté
par deux coordonnées entières.
Ils nous suffira de les déclarer ainsi :
int x ; // abscisse
int y ; // ordonnée
Déclaration des attributs
11
Les attributs sont des variables « globales » au module que constitue
la classe :
Ils sont directement accessibles dans toutes les méthodes de la
classe.
On parle de « portée de classe » (« variables globales à la classe »).
Il n’est donc pas nécessaire de les passer comme arguments des
méthodes.
Portée des attributs
1212
Exemple:
class Point
{
double x;
double y;
void translater(double dx, double dy){
x += dx;
y += dy;
}
double distance() {
return sqrt(x*x+y*y);
}
};
Portée des attributs
1313
Définition des méthodes
Une implémentation (ou définition) de méthode définit du code
exécutable qui peut être invoqué, en passant éventuellement un
nombre fixé de valeurs comme arguments.
Les méthodes sont :
des fonctions propres à la classe
qui ont accès aux attributs de la classe
1414
Définition des méthodes
La syntaxe de la définition des méthodes d’une classe est la syntaxe
normale de définition des fonctions :
Syntaxe :
<typeDeRetour> nomDeLaMethode(<liste_des_paramètres> ) {
<corps de la méthode>
}
Elles sont simplement définies dans la classe elle-même.
1515
Définition des méthodes
<typeDeRetour>
Quand la méthode renvoie une valeur indique le type de la valeur
renvoyée (type simple ou nom d'une classe)
double min(double a, double b)
vector<int> premiers(int n)
void si la méthode ne renvoie pas de valeur (procédure):
void afficher(double m[], int N)
1616
Définition des méthodes
<liste_des_paramètres>
Vide si la méthode n’a pas de paramètres
void afficher()
Une suite de couples type identificateur séparés par des virgules
double max(double a, double b)
double moyenne(array<double, 7> tab)
1717
Définition des méthodes
<corps de la méthode>
Suite de déclarations de variables locales et d’instructions
Si la méthode a un type de retour le corps de la méthode doit contenir
au moins une instruction return expression où expression délivre une
valeur compatible avec le type de retour déclaré.
double min(double a, double b) {
double vMin; // variable locale
if (a < b) vMin = a;
else vMin = b;
return vMin; //instruction de retour
}
1818
Définition des méthodes
return sert aussi à sortir d’une méthode sans renvoyer de valeur
(méthode ayant void comme type retour).
void afficherPosition(double tab[], int taille, double val) {
for(int i(0); i < taille; i++){
if (tab[i] == val){
cout << "La position de " << val << " est " << i << endl;
return;
}
}
cout << val << " n’est pas présente dans le tableau" << endl;
}
1919
Définition des méthodes
Les variables locales sont des variables déclarées à l’intérieur d’une
méthode:
elles conservent les données qui sont manipulées par la méthode.
elles ne sont accessibles que dans le bloc dans lequel elles ont été
déclarées, et leur valeur est perdue lorsque la méthode termine
son exécution.
void method1(...) {
int i;
double y;
int tab[5];
...
}
double method2(...) {
double x;
double y;
double tab[5];
...
}
Possibilité d’utiliser le même identificateur dans
deux méthodes distinctes pas de conflit, c’est la
déclaration locale qui est utilisée dans le corps
de la méthode.
2020
La récursivité des méthodes
C++ autorise la récursivité des appels de méthodes.
Exemple:
unsigned long facto(unsigned int n){
if (n == 0) return 1;
else return facto(n-1)*n;
}
2121
Définition des méthodes
Exemple 1:
La méthode surface() de la classe Rectangle :
class Rectangle{
double hauteur;
double largeur;
double surface(){
return hauteur * largeur;
}
//…
};
Il ne faut pas passer les attributs comme arguments aux méthodes de la
classe.
2222
Définition des méthodes
Exemple 2 :
Les méthodes peuvent avoir des paramètres :
class Point{
// attributs
int x;
int y;
// méthodes
void initialise(int abs, int ord){
x = abs ;
y = ord ;
}
//…
};
2323
Définition des méthodes
Exemple 3 :
Les méthodes peuvent avoir des paramètres :
class Test{
//…
//…
double min(double a, double b) {
if (a < b) return a;
else return b;
}
};
2424
Portée des variables
De manière générale
Variable visible à l'intérieur du bloc (ensembles des instructions entre
{ … }) où elle est définie.
class Visibilite{
int x;
void methodeA() {
float z, w; ...
i = …;
}
void methodeB(float y) {
int z;
do {
...
float w;
z++;
} while (z < i);
x = z +w;
}
};
x : int z : float w : float
x : int y : float z : int
x : int y : float z : int w: float
w: non défini
2525
Portée des variables
Remarque:
La redéfinition d’une variable masque la définition au niveau du bloc
englobant.
class Visibilite {
int x;
void methodeA() {
float z, w; ...
i = …;
}
void methodeB(float y) {
int z;
float x;
do {
...
float w;
z++;
} while (z < i);
x = z +…;
}
};
x : int z : float w : float
x : float y : float z : int
x : float y : float z : int w: float
2626
Définition externe des méthodes
Il est possible d’écrire les définitions des méthodes à l’extérieur de
la déclaration de la classe.
Meilleure lisibilité du code, modularisation
Pour relier la définition d’une méthode à la classe pour laquelle elle
est définie, il suffit d’utiliser l’opérateur :: de résolution de portée :
La déclaration de la classe contient les prototypes des méthodes
Les définitions correspondantes spécifiées à l’extérieur de la
déclaration de la classe se font sous la forme :
typeDeRetour NomDeLaClasse::nomMethode(<liste_des_paramètres>)
{
<corps de la méthode>
}
2727
Définition externe des méthodes
28
Exemple 1:
La méthode surface() de la classe Rectangle définie externe :
class Rectangle {
double hauteur;
double largeur;
double surface(); // prototype
//...
};
// définition de la méthode
double Rectangle::surface(){
return hauteur * largeur;
}
28
Actions et Prédicats
29
En C++, on peut distinguer les méthodes qui modifient l’état de l’objet
(« actions ») de celles qui ne changent rien à l’objet (« prédicats »).
On peut pour cela ajouter le mot const après la liste des paramètres de
la méthode :
typeDeRetour nomDeLaMethode(liste_des_paramètres) const
29
Actions et Prédicats
30
Exemple:
class Rectangle {
double hauteur;
double largeur;
double surface() const; // prototype
//...
};
// définition de la méthode
double Rectangle::surface() const
{
return hauteur * largeur;
}
30
Déclaration d’objets
31
Déclaration d’un objet (ou une instance d’une classe) se fait de
façon similaire à la déclaration d’une variable:
NomDeLaClasse nom_instance;
Exemple 1:
Rectangle rect; //déclare une instance rect de la classe Rectangle.
Point p; //déclare une instance p de la classe Point.
31
Utilisation des objets
Pour accéder aux membres d'un objet on utilise une notation
pointée (.):
nom_instance.nom_du_membre
Le membre pouvant être soit un attribut soit une méthode.
3232
Accès aux attributs
33
L’accès aux valeurs des attributs d’une instance de nom
nom_instance se fait comme pour accéder aux champs d’une
structure :
nom_instance.nom_attribut
Exemple :
La valeur de l’attribut hauteur d’une instance rect de la classe
Rectangle sera référencée par l’expression :
rect.hauteur
33
Accès aux attributs
34
Exemple :
#include <iostream>
using namespace std;
class Rectangle {
double hauteur;
double largeur;
};
int main(){
Rectangle rect;
rect.hauteur = 5.0;
rect.largeur = 6.0;
cout << "Hauteur : " << rect.hauteur << endl;
cout << "Largeur : " << rect.largeur << endl;
return 0;
}
34
Appel aux méthodes
35
L’appel aux méthodes définies pour une instance de nom
nom_instance se fait à l’aide d’expressions de la forme :
nom_instance.nom_methode(arg1,…)
Exemples:
rect.surface() //rect est une instance de la classe Rectangle
tableau.size()
35
Appel aux méthodes
36
Exemple:
// ...
class Rectangle {
double hauteur;
double largeur;
double surface() const{
return hauteur * largeur; }
};
int main(){
Rectangle rect;
rect.hauteur = 5.0;
rect.largeur = 6.0;
cout << " Surface du rectangle = " << rect.surface() << endl;
// ...
}
36
Accès aux attributs et méthodes
37
Chaque instance a ses propres attributs : aucun risque de confusion d’une
instance à une autre.
rect1.surface() : méthode surface de la classe Rectangle s’appliquant à rect1
rect1.surface() Rectangle::surface(&rect1)
37
Encapsulation
3838
Encapsulation et interface
39
Tout ce qu’il n’est pas nécessaire de connaître à l’extérieur d’un objet
devrait être dans le corps de l’objet et identifié par le mot clé private :
class Rectangle
{
double surface() const;
// Tout ce qui suit est privé (inaccessible depuis l'extérieur)
private:
double hauteur;
double largeur;
};
Attribut d’instance privée = inaccessible depuis l’extérieur de la classe.
C’est également valable pour les méthodes.
Si aucun droit d’accès n’est précisé, par défaut, tous les éléments d'une
classe sont private.
39
Encapsulation et interface
40
L’interface, qui est accessible de l’extérieur, se déclare avec le mot-clé public:
class Rectangle
{
// Tout ce qui suit est public (accessible depuis l'extérieur)
public:
double surface() const;
// Tout ce qui suit est privé (inaccessible depuis l'extérieur)
private:
// ...
};
40
Encapsulation et interface
41
Best practice: dans la plupart des cas :
Privé :
Tous les attributs (encapsulation)
La plupart des méthodes
Public :
Quelques méthodes bien choisies (interface)
Règle d'or en POO:
Encapsulation : tous les attributs d'une classe doivent toujours être
privés.
41
Encapsulation et interface
42
En règle générale, il est recommandé de masquer les attributs
(champs) en les déclarant private et -seulement si nécessaire- de
définir des méthodes publiques pour accéder à ces attributs.
Ces méthodes sont appelées accesseurs/mutateurs ou
getters/setters, généralement nommées getxxx() et setxxx().
Il est utile de proposer des méthodes pour accéder aux attributs:
Lecture = getter
Écriture = setter
42
Encapsulation et interface
43
Getters/Setters
Donc, si le programmeur le juge utile, il inclut les méthodes publiques nécessaires:
Accesseurs (« méthodes get » ou « getters ») :
Consultation (i.e. « prédicat »)
Retour de la valeur d’une variable d’instance précise
Exemple 1:
double getHauteur() const { return hauteur; }
double getLargeur() const { return largeur; }
43
Accesseurs et manipulateurs
44
Getters/Setters
Manipulateurs (« mutateurs » ou « méthodes set » ou « setters ») :
Modification (i.e. « action »)
Affectation de l’argument à une variable d’instance précise
Exemple:
void setHauteur(double h) { hauteur = h; }
void setLargeur(double l) { largeur = l; }
44
Accesseurs et manipulateurs
45
Getters/Setters
Exemple:
#include <iostream>
using namespace std;
class Rectangle{
public:
double surface() const{ return hauteur * largeur; }
double getHauteur() const{ return hauteur; }
double getLargeur() const{ return largeur; }
void setHauteur(double h){ hauteur = h; }
void setLargeur(double l){ largeur = l; }
private:
double hauteur;
double largeur;
};
45
Accesseurs et manipulateurs
46
Exemple: (suite)
int main(){
Rectangle rect;
rect.setHauteur(3.0);
rect.setLargeur(4.0);
cout << "Hauteur : " << rect.getHauteur() << endl;
cout << "Largeur : " << rect.getLargeur() << endl;
return 0;
}
Output:
Hauteur: 3
Largeur: 4
46
Masquage/Shadowing
47
masquage = un identificateur « cache » un autre identificateur
int main(){
int i(12);
for (int i(0); i < MAX; ++i) {
cout << i << endl;
}
cout << i << endl;
return 0;
}
47
Masquage/Shadowing
48
Situation typique en POO :
Un paramètre cache un attribut:
//…
class Rectangle{
public:
void setHauteur(double hauteur) //paramètre hauteur
{
hauteur = hauteur; //ambiguïté
}
//…
private:
double hauteur; //attribut hauteur
double largeur;
};
48
Masquage et pointeur this
49
Si, dans une méthode, un attribut est masqué alors la valeur de
l’attribut peut quand même être référencée à l’aide du mot réservé
this.
this est un pointeur sur l’instance courante
this « adresse de l’objet courant »
Syntaxe pour spécifier un attribut en cas d’ambiguïté :
this -> nom_attribut
Exemple :
void setHauteur(double hauteur) {
this->hauteur = hauteur;
}
L’utilisation de this est obligatoire dans les situations de masquage.
49
Pointeur sur l’objet courant : this
Exemple 1: this et variables d’instance
Implicitement quand dans le corps
d’une méthode un attribut est utilisé,
c’est un attribut de l’objet courant.
this essentiellement utilisé pour
lever les ambiguïtés.
50
class Point{
double x;
double y;
void translater(int dx, int dy) {
x += dx;
y += dy;
}
double distance() {
return sqrt(x*x+y*y);
}
void placerAuPoint(double x, double y1){
this->x = x;
y = y1;
}
};
50
Pointeur sur l’objet courant : this
Exemple 2: this et variables d’instance
int main(){
Point a(1, 3) ;
Point b(2, 5) ;
Point c(1, 3) ;
cout << "a et b : " << a.coincide(b) <<endl;
cout << "a et c : " << a.coincide(c) <<endl;
}
51
class Point{
private:
int x, y;
public Point(int abs, int ord){
x = abs ;
y = ord ;
}
public:
bool coincide(Point pt){
return ((pt.x == x) && (pt.y == y)) ;
}
};
51
Pointeur sur l’objet courant : this
Exemple 2: this et variables d’instance
52
class Point{
private:
int x, y;
public Point(int abs, int ord){
x = abs ;
y = ord ;
}
public:
bool coincide(Point pt){
return ((pt.x == this->x) && (pt.y == this->y)) ;
}
};
int main(){
Point a(1, 3) ;
Point b(2, 5) ;
Point c(1, 3) ;
cout << "a et b : " << a.coincide(b) <<endl;
cout << "a et c : " << a.coincide(c) <<endl;
}
52
Classes et Objets
53
Exemple 1:
#include <iostream>
using namespace std;
// définition de la classe
class Rectangle{
// définition des méthodes
public:
double surface() const { return hauteur * largeur; }
double getHauteur() const { return hauteur; } //Accesseur correspondant à hauteur.
double getLargeur() const { return largeur; } //Accesseur correspondant à largeur.
void setHauteur(double h) { hauteur = h; } //Manipulateur pour hauteur
void setLargeur(double l) { largeur = l; } //Manipulateur pour hauteur
// déclaration des attributs
private:
double hauteur;
double largeur;
};
53
Classes et Objets
54
Exemple 1 : (suite)
//utilisation de la classe
int main(){
Rectangle rect;
double h, l;
cout << "Saisir la hauteur du rectangle ? " << endl;
cin >> h;
rect.setHauteur(h);
cout << "Saisir la largeur du rectangle ? " << endl;
cin >> l;
rect.setLargeur(l);
cout << "Surface du rectangle = " << rect.surface() << endl;
return 0;
}
54
55
Exemple 1:
Output:
Saisir la hauteur du rectangle ?
6
Saisir la largeur du rectangle ?
7
Surface du rectangle = 42
Classes et Objets
55
56
Définitions externes à la classe
Exemple 2:
//Prototype de la classe
class Rectangle{
public:
// prototypes des méthodes
double surface() const;
// accesseurs
double getHauteur() const;
double getLargeur() const;
// manipulateurs
void setHauteur(double);
void setLargeur(double);
private:
// déclaration des attributs
double hauteur;
double largeur; };
Classes et Objets
56
57
Définitions externes à la classe
Exemple 2: (suite)
//Définition de la classe
double Rectangle::surface() const {
return hauteur*largeur;
}
double Rectangle::getHauteur() const{
return hauteur;
}
double Rectangle::getLargeur() const{
return largeur;
}
void Rectangle::setHauteur(double h){
hauteur = h;
}
void Rectangle::setLargeur(double l){
largeur = l;
}
Classes et Objets
57
Best practice (1/2)
58
Définitions externes à la classe
//Prototype de la classe:
class Rectangle{
public:
// Prototypes des méthodes:
double surface() const;
// accesseurs
double hauteur() const;
double largeur() const;
// manipulateurs
void hauteur(double);
void largeur(double);
private:
// Déclaration des attributs:
double hauteur_;
double largeur_;
};
58
Best practice (2/2)
59
Définitions externes à la classe
//Définition de la classe:
double Rectangle::surface() const{
return hauteur_ * largeur_;}
// définition des accesseurs
double Rectangle::hauteur() const{
return hauteur_;}
double Rectangle::largeur() const{
return largeur_;}
// définition des manipulateurs
void Rectangle::hauteur(double h){
hauteur_ = h;
}
void Rectangle::largeur(double l){
largeur_ = h;
}
59
Constructeurs
60
Initialisation des attributs
Première solution : affecter individuellement une valeur à chaque attribut.
Exemple:
class Rectangle {
public:
double surface() const;
double getHauteur() const;
double getLargeur() const;
void setHauteur(double h);
void setLargeur(double l);
private:
double hauteur;
double largeur;
};
60
Constructeurs
61
Initialisation des attributs
Première solution : affecter individuellement une valeur à chaque attribut.
Exemple: (suite)
Rectangle rect;
double h, l;
cout << "Quelle hauteur ? ";
cin >> h;
rect.setHauteur(h);
cout << "Quelle largeur ? ";
cin >> l;
rect.setLargeur(l);
61
Constructeurs
62
Initialisation des attributs
Première solution : affecter individuellement une valeur à chaque attribut.
Ceci est une mauvaise solution dans le cas général :
elle implique que tous les attributs fassent partie de l’interface (public) ou soient
assortis d’un manipulateur.
casse l’encapsulation
oblige le programmeur-utilisateur de la classe à initialiser explicitement tous
les attributs.
risque d’oubli
62
Constructeurs
63
Initialisation des attributs
Deuxième solution : définir une méthode dédiée à l’initialisation des
attributs:
class Rectangle{
public:
void init(double h, double L){
hauteur = h;
largeur = L;
}
...
private:
double hauteur;
double largeur;
};
63
Constructeurs
64
Pour faire ces initialisations, il existe en C++ des méthodes
particulières appelées constructeurs.
Un constructeur est une méthode :
invoquée automatiquement lors de la déclaration d’un objet;
chargée d’effectuer toutes les opérations requises en « début de vie »
de l’objet (dont l’initialisation des attributs).
64
Constructeurs
65
Syntaxe de base
NomDeLaClasse(liste_des_paramètres)
{
/*
initialisation des attributs en utilisant liste_des_paramètres
*/
}
Exemple:
Rectangle(double h, double L){
hauteur = h;
largeur = L;
}
65
Constructeurs
66
Un constructeur est une sorte de méthode qui sera invoquée lors de
la création d’un objet de cette classe.
Un constructeur doit porter le nom de la classe et ne doit pas
comporter de type de retour (pas même void) dans sa déclaration.
Un constructeur sera invoqué automatiquement à chaque fois
qu’une instance est créée.
Le but de constructeur est d’initialiser l’objet (notamment la valeur
de ses champs).
66
Constructeurs
67
Comme les autres méthodes :
les constructeurs peuvent être surchargés
on peut donner des valeurs par défaut à leurs paramètres
Une classe peut donc avoir plusieurs constructeurs, pour peu que
leur liste de paramètres soit différente.
67
Constructeurs
Initialisation par constructeur
La création d'une instance (objet) est simple et répond à la syntaxe
suivante :
Syntaxe :
NomDeLaClasse nom_instance(arg1, ..., argN);
où arg1, ..., argN sont les valeurs des arguments du constructeur.
Exemple :
Rectangle r(18.0, 5.3); // invocation du constructeur à 2 paramètres
6868
Instanciation des objets
69
Exemple (1/2):
class Rectangle {
public:
Rectangle(double h, double L){
m_hauteur = h;
m_largeur = L;
}
double surface() const{
return m_hauteur * m_largeur; }
// accesseurs/modificateurs si nécessaire
// ...
private:
double m_hauteur;
double m_largeur;
};
69
Instanciation des objets
70
Exemple (2/2):
// ...
int main(){
double h, l;
cout << "Quelle hauteur ? ";
cin >> h;
cout << "Quelle largeur ? ";
cin >> l;
Rectangle rect1(h,l); // instanciation de l’objet rect de type Rectangle
cout << "Surface = " << rect1.surface();
Rectangle rect2(4.0, 5.0);
cout << "Surface = " << rect2.surface();
}
70
Appel aux constructeurs des attributs
71
Un constructeur devrait normalement contenir une section d’appel
aux constructeurs des attributs....
...ainsi que l’initialisation des attributs de type de base.
C’est ce qu’on appelle la « liste d’initialisation » du constructeur.
Syntaxe générale:
NomDeLaClasse(liste_des_paramètres)
// liste d’initialisation
: attribut1(...), // appel au constructeur de attribut1
...
, attributN(...) // appel au constructeur de attributN
{
// corps du constructeur contenant d’autres opérations
}
71
Liste d’initialisation
72
Cette section introduite par « : » est optionnelle mais recommandée.
Par ailleurs :
les attributs non-initialisés dans cette section:
• prennent une valeur par défaut si ce sont des objets ;
• restent indéfinis s’ils sont de type de base ;
les attributs initialisés dans cette section peuvent être changés dans
le corps du constructeur.
72
Liste d’initialisation
73
Utilisation mixte de liste d'initialisation et affectations dans le corps
du constructeur.
Exemple:
Rectangle(double h, double L)
: m_hauteur(h) //initialisation
{
// largeur a une valeur indéfinie jusqu'ici
m_largeur = 2.0 * L + h; // par exemple...
// la valeur de largeur est définie à partir d'ici
}
73
Liste d’initialisation
74
Deux petites choses à retenir impérativement concernant les listes
d'initialisation :
Les attributs doivent apparaître dans leur ordre de déclaration.
Les initialisations réalisées dans la liste d'initialisation sont effectuées
avant les instructions situées dans le code du corps du constructeur.
74
Construction des attributs
75
Que se passe-t-il si les attributs sont eux-mêmes des objets ?
class RectangleColor{
private:
Rectangle m_rectangle;
Couleur m_couleur;
//...
};
Mauvaise solution :
RectangleColor(double h, double L, Couleur c){
m_rectangle = Rectangle(h, L);
m_couleur = c;
}
Il faut initialiser directement les attributs en faisant appel à leurs
propres constructeurs !
75
Appel aux constructeurs des attributs
76
Exemple:
class Rectangle{
Rectangle(double h, double L);
// ...
};
class RectangleColor {
RectangleColor(double h, double L, Couleur c)
: rectangle(h, L), couleur(c)
{}
private:
Rectangle m_rectangle;
Couleur m_couleur;
};
76
Constructeur par défaut
77
Le constructeur par défaut est un constructeur qui n’a pas de
paramètre ou dont tous les paramètres ont des valeurs par défaut.
Exemple 1:
// Constructeur par défaut avec liste d’initialisation
Rectangle() : m_hauteur(2.0), m_largeur(3.0)
{}
// Constructeur par défaut sans liste d’initialisation
Rectangle() {
m_hauteur = 2.0;
m_largeur = 3.0;
}
…
int main(){
Rectangle rect1; // sans parenthèses
}
77
Constructeur par défaut
78
Autre façon de faire : regrouper 2 constructeurs en utilisant les
valeurs par défaut des paramètres :
Exemple 2:
// DEUX constructeurs dont le constructeur par défaut
Rectangle(double c = 1.0) : m_hauteur(c), m_largeur(2.0*c)
{}
// 3ème constructeur
Rectangle(double h, double L) : m_hauteur(h), m_largeur(L)
{}
…
int main(){
Rectangle rect1, rect2(5.5), rect3(2.5,3.5);
…
}
78
Constructeur par défaut
79
Exemple 3:
// TROIS constructeurs dont le constructeur par défaut
Rectangle(double h = 1.0, double L = 1.0)
: m_hauteur(h), m_largeur(L)
{}
79
Constructeur par défaut par défaut
80
Si aucun constructeur n’est spécifié, le compilateur génère
automatiquement une version minimale du constructeur par défaut
qui :
appelle le constructeur par défaut des attributs objets.
laisse non initialisés les attributs de type de base.
Dès qu’au moins un constructeur a été spécifié, ce constructeur par
défaut par défaut n’est plus fourni.
Si donc on spécifie un constructeur sans spécifier de constructeur par
défaut, on ne peut plus construire d’objet de cette classe sans les
initialiser puisqu’il n’y a plus de constructeur par défaut.
Depuis C++11, mais on peut le rajouter si on veut.
80
Constructeurs par défaut
81
Exemple (1/4):
81
Constructeurs par défaut
82
Exemple (2/4):
82
Constructeurs par défaut
83
Exemple (3/4):
83
Constructeurs par défaut
84
Exemple (4/4):
84
Réactivation du constructeur par défaut par défaut
85
Dès qu’au moins un constructeur a été spécifié, ce constructeur par
défaut par défaut n’est plus fourni.
C’est très bien si c’est vraiment ce que l’on veut (c’est-à-dire forcer
les utilisateurs de la classe à utiliser nos constructeurs).
Mais si l’on veut quand même avoir le constructeur par défaut par
défaut, depuis C++11 on peut le réactiver en écrivant dans la
définition de la classe :
NomDeLaClasse() = default;
85
Réactivation du constructeur par défaut par défaut
86
Exemple:
class Rectangle {
public:
Rectangle() = default; //réactivation du constructeur par défaut par défaut
// 2ème constructeur
Rectangle(double h, double L) : h_hauteur(h), m_largeur(L) {}
// Méthode d’instance
double surface() const { return m_hauteur * m_largeur; }
// Variables d’instance
private:
double m_hauteur;
double m_largeur;
};
86
Méthodes default et delete depuis C++11
87
Ce que l’on a fait précédemment (= default) pour le constructeur
par défaut se généralise :
à toute méthode (pour laquelle cela est pertinent)
à la suppression de méthode, via la syntaxe « = delete »
Exemple:
class DemoClasse{
public:
double f(double x) { ... }
double f(int) = delete;
};
87
Chaînage des constructeurs
88
C++11 autorise les constructeurs d’une classe à appeler n’importe
quel autre constructeur de cette même classe.
Dans les classes définissant plusieurs constructeurs, un constructeur
peut invoquer un autre constructeur de cette classe.
88
Chaînage des constructeurs
89
Exemple:
class Rectangle{
public:
Rectangle(double h, double L) : m_hauteur(h), m_largeur(L)
{}
Rectangle() : Rectangle(0.0, 0.0)
{}
double surface() const {
return m_hauteur * m_largeur;
}
private:
double m_hauteur;
double m_largeur;
};
89
Initialisation par défaut des attributs
90
C++11 permet de donner directement une valeur par défaut aux
attributs.
Si le constructeur appelé ne modifie pas la valeur de cet attribut,
ce dernier aura alors la valeur indiquée.
Exemple:
class Rectangle {
// ...
private:
double hauteur = 0.0;
double largeur = 0.0;
// ...
};
90
Constructeur de copie
91
C++ offre un moyen de créer la copie d’une instance en utilisant le
constructeur de copie lors de l’initialisation:
Rectangle r1(11.3, 12.5);
Rectangle r2(r1); // constructeur de copie: création de l'objet r2
// et son initialisation avec les données de r1.
Ou
Rectangle r2 = r1; // constructeur de copie: création de l'objet r2
// et son initialisation avec les données de r1.
Ou
Rectangle* r2 = new Rectangle(r1); // constructeur de copie: création de l'objet
// dynamique r2 et son initialisation avec les données de r1.
r1 et r2 sont deux instances distinctes mais ayant des mêmes valeurs
pour leurs attributs.
91
Constructeur de copie
92
Le constructeur de copie est appelé dans trois situations:
1. Lors de l’initialisation d’un objet:
Création d'un nouvel objet initialisé comme étant une copie d'un
objet existant.
2. Lors d’un passage de paramètres par valeur :
double maFonction(Rectangle r); //prototype de maFonction
...
x = maFonction(r1); //appel de maFonction
Copie de r1 dans r donc invocation du constructeur de copie.
3. Lors de retour de fonction par valeur
Rectangle g() {
Rectangle rect;
…
return rect; // retour par valeur de rect
}
92
Constructeur de copie
93
Le constructeur de copie permet d’initialiser une instance en
copiant les attributs d’une autre instance du même type.
Deux syntaxes possibles :
NomDeLaClasse(NomDeLaClasse const& autre) { ... }
ou
NomDeLaClasse(NomDeLaClasse& autre) { ... }
Exemple:
Rectangle(Rectangle const& autre)
: m_hauteur(autre.m_hauteur), m_largeur(autre.m_largeur)
{}
93
Constructeur de copie
94
Le but de ce type de constructeur est d'initialiser un objet lors de
son instanciation à partir d'un autre objet.
Toute classe dispose d'un constructeur de copie par défaut
généré automatiquement par le compilateur s’il n’est pas
explicitement défini (constructeur de copie par défaut).
Le seul but est de recopier les attributs de l'objet un à un dans
attributs de l'objet à instancier (si l’attribut est un objet le
constructeur de cet objet est invoqué).
Donc, le rôle du constructeur de copie est de copier la valeur de
tous les attributs du premier objet dans le second.
94
Constructeur de copie
95
Ce constructeur se contente d'effectuer une copie de chacun
des membres. On retrouve là une situation analogue à celle qui est
mise en place (par défaut) lors d'une affectation entre objets de même
type. Elle posera donc les mêmes problèmes pour les objets contenant
des pointeurs sur des emplacements dynamiques.
On aura simplement affaire à une "copie superficielle", c'est-à-dire
que seules les valeurs des pointeurs seront recopiées, les
emplacements pointés ne le seront pas.
copie de surface ou copie superficielle
95
Constructeur de copie
96
Cette copie de surface suffit dans la plupart des cas.
Cependant, il est parfois nécessaire de redéfinir le constructeur
de copie, en particulier lorsque certains attributs sont des
pointeurs.
Il faut alors allouer une nouvelle zone mémoire, on parle alors de
copie profonde (allocation puis recopie).
96
Constructeur de copie
97
La règle de trois :
Si vous touchez à l’un des trois parmi:
constructeur de copie,
destructeur,
opérateur d’affectation (operator=),
alors pensez aux deux autres.
97
Constructeur de copie
98
Remarque:
Il faut faire attention entre copie et affectation.
Rectangle rect1, rect2; // création de deux objets rect1 et rect2.
rect1 = rect2; // pas de copie mais uniquement l'affectation
// car l’objet est déjà créé.
98
Suppression du constructeur de copie
99
Depuis C++11, si l’on souhaite interdire la copie, il suffit de
supprimer le constructeur de copie par défaut avec la commande
« = delete ».
Exemple:
class Incopiable {
/*... */
Incopiable(Incopiable const&) = delete;
};
99
Destructeur
100
Si l’initialisation des attributs d’une instance implique la mobilisation
de ressources : fichiers, périphériques, portions de mémoire
(pointeurs), etc.
Il est alors important de libérer ces ressources après usage.
Comme pour l’initialisation, l’invocation explicite de méthodes de
libération n’est pas satisfaisante (fastidieuse, source d’erreur,
affaiblissement de l’encapsulation).
100
Destructeur
101
C++ offre une méthode appelée destructeur invoquée
automatiquement en fin de vie de l’instance.
Il s'exécute à la fin du programme ou d'un bloc où des objets locaux
ont été définis.
Un destructeur a pour rôle de libérer la mémoire.
Il est appelé automatiquement lorsque l’objet n’est plus utilisé,
fermeture d’accolade, …
101
Destructeur
102
La syntaxe de déclaration d’un destructeur pour une classe
NomDeLaClasse est :
˜NomDeLaClasse()
{
// opérations (de libération)
}
Le destructeur d’une classe est une méthode sans paramètre donc
pas de surcharge possible
Son nom est celui de la classe, précédé du signe ˜ (tilda).
Si le destructeur n’est pas défini explicitement par le programmeur,
le compilateur en génère automatiquement une version minimale.
102
Constructeur et destructeur
103
Exemple 1:
Supposons que l’on souhaite compter le nombre d’instances d’une
classe actives à un moment donné dans un programme.
int main(){
// compteur = 0
Rectangle r1;
// compteur = 1
{
Rectangle r2;
// compteur = 2
// ...
}
// compteur = 1
return 0;
} // compteur = 0
103
Appel du constructeur
Appel du destructeur pour r2 et r1
Constructeur et destructeur
104
Exemple 1:
Utilisons comme compteur une variable globale de type entier :
Le constructeur incrémente le compteur
long compteur(0);
class Rectangle{
//...
Rectangle(): m_hauteur(0.0), m_largeur(0.0) //constructeur
{
++compteur;
}
// ...
104
Constructeur et destructeur
105
Exemple 1:
On est obligé ici de définir explicitement le destructeur
int main(){
// compteur = 0
Rectangle r1;
// compteur = 1
{
Rectangle r2;
// compteur = 2
// ...
}
// compteur = 2
return 0;
} // compteur = 2
105
Constructeur et destructeur
106
Exemple 1:
Le constructeur incrémente le compteur
Le destructeur le décrémente
long compteur(0);
class Rectangle {
//...
Rectangle(): m_hauteur(0.0), m_largeur(0.0) {//constructeur
++compteur;
}
~Rectangle() {// destructeur
--compteur;
} // ...
106
Constructeur et destructeur
107
Exemple 1:
int main(){
// compteur = 0
Rectangle r1;
// compteur = 1
{
Rectangle r2;
// compteur = 2
// ...
}
// compteur = 1
return 0;
} // compteur = 0
107
Constructeur et destructeur
108
Exemple 1:
Que se passe-il si l’on souhaite utiliser la copie d’objet ?
int main(){
// compteur = 0
Rectangle r1;
// compteur = 1
{
Rectangle r2;
// compteur = 2
Rectangle r3(r2);
// compteur = ??
}
// compteur =
return 0;
} // compteur =
108
Appel du constructeur
Appel du constructeur de copie
Appel du destructeur pour r2, r3 et r1
Constructeur et destructeur
109
Exemple 1:
La copie d’un rectangle échappe au compteur d’instances car il n’y a
pas de définition explicite du constructeur de copie.
Il faudrait donc encore ajouter au code précédent, la définition
explicite du constructeur de copie :
Rectangle(Rectangle const& r)
: m_hauteur(r.m_hauteur), m_largeur(r.m_largeur)
{ ++compteur; }
Règle générale :
Si on doit toucher à l’un des trois parmi destructeur, constructeur
de copie et opérateur d’affectation (=), alors on doit certainement
également toucher aux deux autres (ou alors au moins se poser la
question !).
109
Constructeur et destructeur
110
Exemple 2:
class Chaine{ // Implémente une chaîne de caractères.
private:
char* s; // Le pointeur sur la chaîne de caractères.
public:
Chaine(); // Le constructeur par défaut.
Chaine(int); // Le constructeur. Il n'a pas de type.
~Chaine(); // Le destructeur.
};
Chaine::Chaine(){
s = nullptr; } // La chaîne est initialisée avec le pointeur nullptr.
Chaine::Chaine(int Taille){
s = new char[Taille+1]; // Alloue de la mémoire pour la chaîne.
s[0]='0'; // Initialise la chaîne à "".
}
Chaine::~Chaine(){
if (s!=nullptr) delete[] s; // Restitue la mémoire utilisée si nécessaire.
}
110
Création des instances dynamiques
111
Dans le cas d'une instance dynamique, il faut commencer par déclarer
un pointeur, puis appeler l’opérateur new suivi du nom du constructeur
avec ses paramètres.
Syntaxe:
NomDeLaClasse *nom_instance;
nom_instance = new NomDeLaClasse(arg1,..., argN);
Ou directement
NomDeLaClasse *nom_instance = new NomDeLaClasse(arg1,...,argN);
où arg1, ..., argN sont les valeurs des arguments du constructeur.
111
Création des instances dynamiques
112
Exemple:
Rectangle *r1(nullptr), *r2(nullptr); // Déclaration d'un pointeur sur un objet de type Rectangle
r1 = new Rectangle(4.0, 5.0);// Instanciation de l'objet dynamique avec arguments explicites
r2 = new Rectangle; // Instanciation de l'objet dynamique avec arguments par défaut
delete r1; // On n'oublie pas de rendre la mémoire !
delete r2; // Idem
112
Création des instances dynamiques
113
Appel de méthodes d’instance
La syntaxe d'appel d'un membre d’instance par un objet dynamique est:
nom_instance -> nom_membre
Il faut juste changer le "." par une flèche "->".
Exemple:
Rectangle *rect1 = new Rectangle(2.5, 3.5);
Rectangle *rect2 = new Rectangle;
cout << "Hauteur = "<< rect1-> m_hauteur << endl;
cout << "Largeur = "<< rect1-> m_largeur << endl;
cout << "Surface du rectangle 1 = "<< rect1-> surface() << endl;
cout << "Surface du rectangle 2 = "<< (*rect2).surface() << endl;
113
Envoi de messages
Pour "demander" à un objet d'effectuer une opération (exécuter
l'une de ses méthodes) il faut lui envoyer un message.
Un message est composé de trois parties:
un identificateur permettant de désigner l'objet à qui le
message est envoyé.
le nom de la méthode à exécuter (cette méthode doit bien
entendu être définie dans la classe de l'objet).
les éventuels paramètres de la méthode.
114114
Envoi de messages
Envoi de message similaire à un appel de fonction
les instructions définies dans la méthode sont exécutées (elles
s’appliquent sur les attributs de l’objet récepteur du message).
puis le contrôle est retourné au programme appelant.
Les objets communiquent entre eux par échange de messages.
115115
Envoi de messages
Syntaxe :
nom_instance.nomDeMethode(<liste_paramètres_effectifs>)
Si la méthode ne possède pas de paramètres, la liste est vide, mais comme en langage C les
parenthèses demeurent.
class Point{
private:
double x{0.0};
double y{0.0};
public:
void translater(double dx, double dy){
x += dx;
y += dy;
}
double distance(){
return sqrt(x*x+y*y);
}
}; // Point
Point p1, p2;
p1.translater(10.0,10.0);
cout << “Distance de p2 à l’origine : “<< endl;
cout << p2.distance();
116116
Mise en œuvre d’un programme comportant
plusieurs fichiers
Un fichier entête Classe.h qui définit la classe (appelé interface) va contenir
le prototype de la classe, c.à.d., la déclaration de la classe avec les attributs et
les prototypes des méthodes.
Un fichier source Classe.cpp contient la définition de la classe, c’est là
qu'on va implémenter les méthodes membres.
L’implémentation des méthodes peut se faire dans l’entête.
Un fichier source (.cpp) contient la fonction main().
Exemple:
Point.h: contient le prototype de la classe (interface);
Point.cpp: contient la définition de la classe, c.à.d., l'implémentation des
méthodes membres de la classe.
TestPoint.cpp: contient la fonction main().
117117
Mise en œuvre d’un programme comportant
plusieurs fichiers
Exemple (1/2):
Point.h Point.cpp
#ifndef POINT_H_INCLUDED
#define POINT_H_INCLUDED
class Point
{
public:
void translater(double dx, double dy);
double distance();
void afficher();
private:
double x;
double y;
};
#endif // POINT_H_INCLUDED
#include "Point.h“
#include<iostream>
#include<cmath>
using namespace std;
void Point::translater(double dx, double dy){
x += dx;
y += dy;
}
double Point::distance() {
return sqrt(x*x+y*y);
}
void Point::afficher(){
cout << "(" << x << ", "<< y << ") " << endl;
}
118118
Mise en œuvre d’un programme comportant
plusieurs fichiers
Exemple (2/2):
TestPoint.cpp
#include "Point.h“
#include<iostream>
using namespace std;
int main(){
Point p1, p2;
p1.translater(10.0,10.0);
p1.afficher();
cout << “distance de p2 à l’origine“ << p1.distance() << endl;
}
119119
Mise en œuvre d’un programme comportant
plusieurs classes
Plusieurs classes dans un même fichier source
TestPoint.cpp
120120
#include<iostream>
#include<cmath>
using namespace std;
class Point {
public:
void translater(double dx, double dy); double distance();
void afficher();
private:
double x;
double y;};
void Point::translater(double dx, double dy){ x += dx; y += dy;}
double Point::distance() { return sqrt(x*x+y*y);}
void Point::afficher(){ cout << "(" << x << ", "<< y << ") " << endl; }
int main(){
Point p1, p2;
p1.translater(10.0,10.0); p1.afficher();
cout << “distance de p2 à l’origine“ << p1.distance() << endl; }
Surcharge des méthodes
Surcharge (overloading) pas limitée aux constructeurs, elle est
possible pour n'importe quelle méthode.
Possible de définir des méthodes possédant le même nom mais
dont les arguments diffèrent.
Lorsque qu'une méthode surchargée est invoquée le compilateur
sélectionne automatiquement la méthode dont le nombre et le type
des arguments correspondent au nombre et au type des paramètres
passés dans l'appel de la méthode.
Des méthodes surchargées peuvent avoir des types de retour
identiques mais à condition qu'elles aient des arguments différents.
121121
Surcharge des méthodes
Exemple
class Point{
private:
double x;
double y;
public:
// constructeur
Point(double x, double y): x(x), y(y){}
// destructeur
~Point(){cout<< “Destructeur“ <<endl;}
// méthodes
double distance() { return sqrt(x*x+y*y); }
...
}
double distance(Point p){
return sqrt((x - p.x)*(x - p.x) + (y - p.y)*(y - p.y));
}
…
};
Point p1(10,10);
Point p2(1.5,1.4);
cout<<“Distance =”<<p1.distance();
cout<<“Distance entre p1 et p2 =”;
cout<<p1.distance(p2);
122122
Variables et méthodes d’instance
123123
Variables et méthodes d’instance
Variables d’instance ou attributs d’instance
Déclarées par accès: type nomDeVariableInstance
Référencées par Objet.nomDeVariableInstance
Une copie pour chaque instance (objet)
Chaque objet possède ses propres membres donnée.
Méthodes d’instance
Déclarées par accès: TypeRetour nomDeLaMethodeInstance(…)
Référencées par objet.nomDeLaMethodeInstance(…)
124124
Membres de classe: static
125125
Membres de classe: static
126126
Variables de classe: static
Il peut être utile de définir pour une classe des attributs
indépendamment des instances : nombre de Points crées
Les variables statiques sont enregistrées au niveau de la classe et elles
peuvent être accédées et manipulées par toutes les instances (objets)
de cette classe.
Utilisation des variables de classe comparables aux “variables
globales“ partagées par toutes les instances.
Variables dont il n’existe qu’un seul exemplaire associé à sa classe de
définition.
Variables existent indépendamment du nombre d’instances de la
classe qui ont été créés.
Variables utilisables même si aucune instance de la classe n’existe.
127127
Variables de classe
Déclarées par accès: static type nomDeLaVariableClasse
Référencées par NomDeLaClasse::nomDeLaVariableClasse
Une seule copie pour toutes les instances
Les variables statiques appartiennent à la classe et non aux objets
créés à partir de la classe.
Un attribut static, bien qu'il soit accessible de l'extérieur, peut très
bien être déclaré private ou protected
N. B:
Variables de classe = Variables statiques = Attributs de classe = Attributs statiques.
128128
Variables de classe
Initialisation d’une variable statique
Il faut initialiser l'attribut statique dans l'espace global, c'est-à-dire en
dehors de toute classe ou fonction, en dehors du main() notamment.
Un attribut déclaré comme statique se comporte comme une variable
globale, c'est-à-dire une variable accessible partout dans le code.
Un attribut de classe doit être initialisé explicitement à l’extérieur de
la classe.
Les attributs de classe sont très pratiques lorsque différents objets
d’une classe doivent accéder à une même information.
Ils permettent notamment d’éviter que cette information soit
dupliquée au niveau de chaque objet.
Exemple:
// Initialiser l'attribut en dehors de toute fonction ou classe (espace global)
int MaClasse::attributStatique = 10;
129129
Variables de classe
Exemple:
class Point {
private:
double x;
double y;
static int nbPointCrees;
public:
Point(double x, double y){
this->x = x;
this->y=y;
}
…
};
//initialisation de l’attribut statique
int Point::nbPointCrees=0;
130130
Méthodes de classe
131131
Méthodes de classe
Usage
Ce sont des méthodes qui ne s'intéressent pas à un objet particulier
Utiles pour des calculs intermédiaires internes à une classe
Utiles également pour retourner la valeur d'une variable de classe
Elles sont déclarées comme les méthodes d'instances, mais avec le mot-
clé static:
public: static double vitesseMaxToleree();
Elles sont définies hors de la classe sans static:
double Voiture::vitesseMaxToleree() {
return vitesseMaxAutorisee*1.10; }
Pour y accéder, il faut utiliser non pas un identificateur d'objet mais le
nom de la classe et de l’opérateur de résolution de portée « :: » (idem
pour les variables de classe):
Voiture::vitesseMaxToleree(); // Voiture une classe
132132
Méthodes de classe
Exemple (1/2):
133
#include<iostream>
using namespace std;
class Point {
private:
double x;
double y;
static int compteur;
public:
Point(double x, double y);
~Point();
static int nombreInstances(); //Renvoie le nombre d'objets créés
};
//initialisation de l’attribut statique
int Point::compteur=0;
//Quand on crée un point, on ajoute 1 au compteur
Point:: Point(double x, double y):x(x), y(y){++compteur;}
//Et on enlève 1 au compteur lors de la destruction
Point:: ~Point(){--compteur;}
int Point::nombreInstances(){
return compteur; //On renvoie simplement la valeur du compteur
}
133
Méthodes de classe
Exemple (2/2):
Output:
Il y a actuellement 2 points construits.
134
int main()
{
//On crée deux points
Point p1(1.0, 2.0);
Point p2(3.0, 4.0);
//Et on consulte notre compteur
cout << "Il y a actuellement " << Point::nombreInstances() << " points construits." << endl;
return 0;
}
134
135135
Propriétés des fonctions membres
Les fonctions membres (y compris les constructeurs) peuvent:
être surdéfinies,
avoir des arguments par défaut,
être public ou private,
être inline.
Les fonctions membres ont accès à tous les membres de tous les
objets de la classe.
Surdéfinition des fonctions membre
C++ autorise la surdéfinition (ou surcharge) des fonctions
membres y compris les constructeurs (mais pas aux destructeurs);
Exemple (1/2):
class Point{
private :
double x, y;
public :
Point(); // constructeur 1 sans arguments
Point(double); // constructeur 2 avec un argument
Point(double, double); // constructeur 3 avec deux arguments
void afficher(); // fonction afficher() sans arguments
void afficher(char*); // fonction affiche() avec un argument
};
Propriétés des fonctions membres
136
Propriétés des fonctions membres
Surdéfinition des fonctions membre
Exemple (2/2):
Point::Point(): x(0), y(0){}
Point::Point(double abs) : x(abs), y(abs) {}
Point::Point(double abs, double ord): x(abs), y(ord) {}
void Point::afficher(){
cout << "(" << x << ", " << y << ") " << endl;
}
void Point::afficher(char *msg) {
cout << msg << endl;
afficher();
}
137
Arguments par défaut
Tout comme une fonction C++ classique, il est possible de définir
des arguments par défaut. Ceux-ci permettent à l'utilisateur de ne
pas renseigner certains paramètres.
On peut modifier notre classe Point pour qu’elle ne possède qu’une
seule fonction affiche à un seul argument de type chaîne.
void afficher(char* = " " ); // fonction afficher(): un argument par défaut
void Point::afficher(char *msg) {
cout << msg << "(" << x << "," <<y << ")" <<endl;
}
Point A;
A.afficher(); //affichage: (0, 0)
Point B(1.5, 2.5);
B.afficher(" Point B"); // affichage : Point B(1.5, 2.5)
Propriétés des fonctions membres
138
Les fonctions membre en ligne
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 le qualificatif inline
n’a pas à être utilisé (implicite).
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, le qualificatif inline doit apparaître à la fois
devant la déclaration et devant l’en tête (explicite).
Propriétés des fonctions membres
139
Les fonctions membre en ligne
Exemple:
class Rectangle{
…
public :
inline Rectangle();
…
}; // Fin de la classe
inline Rectangle(): hauteur(0.0), largeur(0.0)
{
}
Propriétés des fonctions membres
140
Typologie des méthodes d’une classe
141
Parmi les différentes méthodes que comporte une classe, on a souvent
tendance à distinguer :
Les constructeurs et les destructeurs;
Les méthodes d’accès (en anglais accessor/getter), en bon français
accesseurs, qui fournissent des informations relatives à l’état d’un
objet, c’est-à-dire aux valeurs de certains de ses champs (généralement
privés), sans les modifier ;
Les méthodes d’altération (en anglais mutator/setter), en bon
français manipulateur ou mutateur, qui modifient l’état d’un objet,
donc les valeurs de certains de ses champs.
141
142
Q & A
142

More Related Content

What's hot

Atelier Python 2eme partie par Achraf Kacimi El Hassani
Atelier Python 2eme partie par Achraf Kacimi El HassaniAtelier Python 2eme partie par Achraf Kacimi El Hassani
Atelier Python 2eme partie par Achraf Kacimi El Hassani
Shellmates
 
Cours python
Cours pythonCours python
Cours python
salmazen
 

What's hot (20)

Python avancé : Interface graphique et programmation évènementielle
Python avancé : Interface graphique et programmation évènementiellePython avancé : Interface graphique et programmation évènementielle
Python avancé : Interface graphique et programmation évènementielle
 
Formation python
Formation pythonFormation python
Formation python
 
Programmation orientée objet : Object, classe et encapsulation
Programmation orientée objet : Object, classe et encapsulationProgrammation orientée objet : Object, classe et encapsulation
Programmation orientée objet : Object, classe et encapsulation
 
COURS_PYTHON_22.ppt
COURS_PYTHON_22.pptCOURS_PYTHON_22.ppt
COURS_PYTHON_22.ppt
 
Chap2fonctionscpp
Chap2fonctionscppChap2fonctionscpp
Chap2fonctionscpp
 
Partie 13: Héritage Multiple — Programmation orientée objet en C++
Partie 13: Héritage Multiple — Programmation orientée objet en C++Partie 13: Héritage Multiple — Programmation orientée objet en C++
Partie 13: Héritage Multiple — Programmation orientée objet en C++
 
Chapitre3TableauxEnCppV2019
Chapitre3TableauxEnCppV2019Chapitre3TableauxEnCppV2019
Chapitre3TableauxEnCppV2019
 
Introduction à Python
Introduction à PythonIntroduction à Python
Introduction à Python
 
Cours de c
Cours de cCours de c
Cours de c
 
Chap 6 : classes et interfaces
Chap 6 : classes et interfacesChap 6 : classes et interfaces
Chap 6 : classes et interfaces
 
Atelier Python 2eme partie par Achraf Kacimi El Hassani
Atelier Python 2eme partie par Achraf Kacimi El HassaniAtelier Python 2eme partie par Achraf Kacimi El Hassani
Atelier Python 2eme partie par Achraf Kacimi El Hassani
 
La gestion des exceptions avec Java
La gestion des exceptions avec JavaLa gestion des exceptions avec Java
La gestion des exceptions avec Java
 
Cours structures des données (langage c)
Cours structures des données (langage c)Cours structures des données (langage c)
Cours structures des données (langage c)
 
FormationPython2019.pptx
FormationPython2019.pptxFormationPython2019.pptx
FormationPython2019.pptx
 
Chapitre8: Collections et Enumerations En Java
Chapitre8: Collections et Enumerations En JavaChapitre8: Collections et Enumerations En Java
Chapitre8: Collections et Enumerations En Java
 
Cours python
Cours pythonCours python
Cours python
 
Partie 11: Héritage — Programmation orientée objet en C++
Partie 11: Héritage — Programmation orientée objet en C++Partie 11: Héritage — Programmation orientée objet en C++
Partie 11: Héritage — Programmation orientée objet en C++
 
Cours Programmation Orientée Objet en C++
Cours Programmation Orientée Objet en C++Cours Programmation Orientée Objet en C++
Cours Programmation Orientée Objet en C++
 
Chapitre1: Langage Python
Chapitre1: Langage PythonChapitre1: Langage Python
Chapitre1: Langage Python
 
Cours de programmation en c
Cours de programmation en cCours de programmation en c
Cours de programmation en c
 

Similar to Chapitre5: Classes et objets

Interface collectionsinter
Interface collectionsinterInterface collectionsinter
Interface collectionsinter
RYMAA
 

Similar to Chapitre5: Classes et objets (20)

Cpp2 : classes et objets
Cpp2 : classes et objetsCpp2 : classes et objets
Cpp2 : classes et objets
 
Chapitre 2 classe et objet
Chapitre 2   classe et objetChapitre 2   classe et objet
Chapitre 2 classe et objet
 
POO-chapitre3.pptx
POO-chapitre3.pptxPOO-chapitre3.pptx
POO-chapitre3.pptx
 
POO-JAVA-partie-1.pdf
POO-JAVA-partie-1.pdfPOO-JAVA-partie-1.pdf
POO-JAVA-partie-1.pdf
 
Chapitre3 2013 POO
Chapitre3 2013 POOChapitre3 2013 POO
Chapitre3 2013 POO
 
Diagramme de Classe
Diagramme de ClasseDiagramme de Classe
Diagramme de Classe
 
Chap 2--POO avec JAVA.pdf
Chap 2--POO avec JAVA.pdfChap 2--POO avec JAVA.pdf
Chap 2--POO avec JAVA.pdf
 
22-reflection.pdf
22-reflection.pdf22-reflection.pdf
22-reflection.pdf
 
Memo java
Memo javaMemo java
Memo java
 
Héritage et Polymorphisme .pdf
Héritage et Polymorphisme .pdfHéritage et Polymorphisme .pdf
Héritage et Polymorphisme .pdf
 
Interface collectionsinter
Interface collectionsinterInterface collectionsinter
Interface collectionsinter
 
Chap 03 poo en java partie2
Chap 03 poo en java partie2Chap 03 poo en java partie2
Chap 03 poo en java partie2
 
POO
POOPOO
POO
 
Ch06
Ch06Ch06
Ch06
 
Pensez objets avec java
Pensez objets avec javaPensez objets avec java
Pensez objets avec java
 
Python avancé : Classe et objet
Python avancé : Classe et objetPython avancé : Classe et objet
Python avancé : Classe et objet
 
Ch03
Ch03Ch03
Ch03
 
Chp6 - De UML vers C++
Chp6 - De UML vers C++Chp6 - De UML vers C++
Chp6 - De UML vers C++
 
Ch10
Ch10Ch10
Ch10
 
Chapitre 1 rappel
Chapitre 1   rappelChapitre 1   rappel
Chapitre 1 rappel
 

More from Aziz Darouichi

More from Aziz Darouichi (7)

Chapitre 2: String en Java
Chapitre 2:  String en JavaChapitre 2:  String en Java
Chapitre 2: String en Java
 
Chapitre 11: Expression Lambda et Référence de méthode en Java
Chapitre 11: Expression Lambda et Référence de méthode en JavaChapitre 11: Expression Lambda et Référence de méthode en Java
Chapitre 11: Expression Lambda et Référence de méthode en Java
 
Partie3BI-DW-OLAP2019
Partie3BI-DW-OLAP2019Partie3BI-DW-OLAP2019
Partie3BI-DW-OLAP2019
 
Partie2BI-DW2019
Partie2BI-DW2019Partie2BI-DW2019
Partie2BI-DW2019
 
Partie1BI-DW2019
Partie1BI-DW2019Partie1BI-DW2019
Partie1BI-DW2019
 
Cours Visual Basic.NET
Cours Visual Basic.NETCours Visual Basic.NET
Cours Visual Basic.NET
 
Chapitre3 tableauxcpp
Chapitre3 tableauxcppChapitre3 tableauxcpp
Chapitre3 tableauxcpp
 

Chapitre5: Classes et objets

  • 1. Aziz DAROUICHI 1 Programmation Orientée Objet en C++: Classes et Objets
  • 2. 2 Notion de classe Portée des attributs Définition des méthodes, Définition externe des méthodes Actions et Prédicats Déclaration et utilisation d’objets Appel aux méthodes Accès aux attributs et méthodes Encapsulation et interface Accesseurs et manipulateurs Masquage/Shadowing Pointeur sur l’objet courant : this Constructeur, Liste d’initialisation, Chainage des constructeurs Constructeur de copie, Destructeur Création des instances dynamiques Membres de classe Typologie des méthodes d’une classe Q & A Notion de classe Portée des attributs Définition des méthodes, Définition externe des méthodes Actions et Prédicats Déclaration et utilisation d’objets Appel aux méthodes Accès aux attributs et méthodes Encapsulation et interface Accesseurs et manipulateurs Masquage/Shadowing Pointeur sur l’objet courant : this Constructeur, Liste d’initialisation, Chainage des constructeurs Constructeur de copie, Destructeur Création des instances dynamiques Membres de classe Typologie des méthodes d’une classe Q & A Chapitre 5: Classes et Objets 2
  • 6. 6 Définition d’une classe // N'oubliez pas le point-virgule à la fin ! 6
  • 7. 7 Définition d’une classe Exemple 1: class Rectangle { double hauteur; double largeur; double surface(){ return hauteur*largeur; } double perimetre() { return 2*(hauteur+largeur); } }; Nom de la classe Attributs/champs (fields) Méthodes 7
  • 8. 8 Définition d’une classe Exemple 2: … class Point { double x; double y; void translater(double dx, double dy){ x += dx; y += dy; } double distance() { return sqrt(x*x+y*y); } }; Attributs/champs (fields) Nom de la classe Méthodes 8
  • 10. 10 Attributs : c'est le nom que l'on donne aux variables contenues dans des classes. Exemple 1: Les attributs hauteur et largeur, de type double, de la classe Rectangle pourront être déclarés par : class Rectangle { double hauteur; double largeur; }; Déclaration des attributs 10
  • 11. 11 Exemple 2: Nous supposerons ici qu’un objet de type Point sera représenté par deux coordonnées entières. Ils nous suffira de les déclarer ainsi : int x ; // abscisse int y ; // ordonnée Déclaration des attributs 11
  • 12. Les attributs sont des variables « globales » au module que constitue la classe : Ils sont directement accessibles dans toutes les méthodes de la classe. On parle de « portée de classe » (« variables globales à la classe »). Il n’est donc pas nécessaire de les passer comme arguments des méthodes. Portée des attributs 1212
  • 13. Exemple: class Point { double x; double y; void translater(double dx, double dy){ x += dx; y += dy; } double distance() { return sqrt(x*x+y*y); } }; Portée des attributs 1313
  • 14. Définition des méthodes Une implémentation (ou définition) de méthode définit du code exécutable qui peut être invoqué, en passant éventuellement un nombre fixé de valeurs comme arguments. Les méthodes sont : des fonctions propres à la classe qui ont accès aux attributs de la classe 1414
  • 15. Définition des méthodes La syntaxe de la définition des méthodes d’une classe est la syntaxe normale de définition des fonctions : Syntaxe : <typeDeRetour> nomDeLaMethode(<liste_des_paramètres> ) { <corps de la méthode> } Elles sont simplement définies dans la classe elle-même. 1515
  • 16. Définition des méthodes <typeDeRetour> Quand la méthode renvoie une valeur indique le type de la valeur renvoyée (type simple ou nom d'une classe) double min(double a, double b) vector<int> premiers(int n) void si la méthode ne renvoie pas de valeur (procédure): void afficher(double m[], int N) 1616
  • 17. Définition des méthodes <liste_des_paramètres> Vide si la méthode n’a pas de paramètres void afficher() Une suite de couples type identificateur séparés par des virgules double max(double a, double b) double moyenne(array<double, 7> tab) 1717
  • 18. Définition des méthodes <corps de la méthode> Suite de déclarations de variables locales et d’instructions Si la méthode a un type de retour le corps de la méthode doit contenir au moins une instruction return expression où expression délivre une valeur compatible avec le type de retour déclaré. double min(double a, double b) { double vMin; // variable locale if (a < b) vMin = a; else vMin = b; return vMin; //instruction de retour } 1818
  • 19. Définition des méthodes return sert aussi à sortir d’une méthode sans renvoyer de valeur (méthode ayant void comme type retour). void afficherPosition(double tab[], int taille, double val) { for(int i(0); i < taille; i++){ if (tab[i] == val){ cout << "La position de " << val << " est " << i << endl; return; } } cout << val << " n’est pas présente dans le tableau" << endl; } 1919
  • 20. Définition des méthodes Les variables locales sont des variables déclarées à l’intérieur d’une méthode: elles conservent les données qui sont manipulées par la méthode. elles ne sont accessibles que dans le bloc dans lequel elles ont été déclarées, et leur valeur est perdue lorsque la méthode termine son exécution. void method1(...) { int i; double y; int tab[5]; ... } double method2(...) { double x; double y; double tab[5]; ... } Possibilité d’utiliser le même identificateur dans deux méthodes distinctes pas de conflit, c’est la déclaration locale qui est utilisée dans le corps de la méthode. 2020
  • 21. La récursivité des méthodes C++ autorise la récursivité des appels de méthodes. Exemple: unsigned long facto(unsigned int n){ if (n == 0) return 1; else return facto(n-1)*n; } 2121
  • 22. Définition des méthodes Exemple 1: La méthode surface() de la classe Rectangle : class Rectangle{ double hauteur; double largeur; double surface(){ return hauteur * largeur; } //… }; Il ne faut pas passer les attributs comme arguments aux méthodes de la classe. 2222
  • 23. Définition des méthodes Exemple 2 : Les méthodes peuvent avoir des paramètres : class Point{ // attributs int x; int y; // méthodes void initialise(int abs, int ord){ x = abs ; y = ord ; } //… }; 2323
  • 24. Définition des méthodes Exemple 3 : Les méthodes peuvent avoir des paramètres : class Test{ //… //… double min(double a, double b) { if (a < b) return a; else return b; } }; 2424
  • 25. Portée des variables De manière générale Variable visible à l'intérieur du bloc (ensembles des instructions entre { … }) où elle est définie. class Visibilite{ int x; void methodeA() { float z, w; ... i = …; } void methodeB(float y) { int z; do { ... float w; z++; } while (z < i); x = z +w; } }; x : int z : float w : float x : int y : float z : int x : int y : float z : int w: float w: non défini 2525
  • 26. Portée des variables Remarque: La redéfinition d’une variable masque la définition au niveau du bloc englobant. class Visibilite { int x; void methodeA() { float z, w; ... i = …; } void methodeB(float y) { int z; float x; do { ... float w; z++; } while (z < i); x = z +…; } }; x : int z : float w : float x : float y : float z : int x : float y : float z : int w: float 2626
  • 27. Définition externe des méthodes Il est possible d’écrire les définitions des méthodes à l’extérieur de la déclaration de la classe. Meilleure lisibilité du code, modularisation Pour relier la définition d’une méthode à la classe pour laquelle elle est définie, il suffit d’utiliser l’opérateur :: de résolution de portée : La déclaration de la classe contient les prototypes des méthodes Les définitions correspondantes spécifiées à l’extérieur de la déclaration de la classe se font sous la forme : typeDeRetour NomDeLaClasse::nomMethode(<liste_des_paramètres>) { <corps de la méthode> } 2727
  • 28. Définition externe des méthodes 28 Exemple 1: La méthode surface() de la classe Rectangle définie externe : class Rectangle { double hauteur; double largeur; double surface(); // prototype //... }; // définition de la méthode double Rectangle::surface(){ return hauteur * largeur; } 28
  • 29. Actions et Prédicats 29 En C++, on peut distinguer les méthodes qui modifient l’état de l’objet (« actions ») de celles qui ne changent rien à l’objet (« prédicats »). On peut pour cela ajouter le mot const après la liste des paramètres de la méthode : typeDeRetour nomDeLaMethode(liste_des_paramètres) const 29
  • 30. Actions et Prédicats 30 Exemple: class Rectangle { double hauteur; double largeur; double surface() const; // prototype //... }; // définition de la méthode double Rectangle::surface() const { return hauteur * largeur; } 30
  • 31. Déclaration d’objets 31 Déclaration d’un objet (ou une instance d’une classe) se fait de façon similaire à la déclaration d’une variable: NomDeLaClasse nom_instance; Exemple 1: Rectangle rect; //déclare une instance rect de la classe Rectangle. Point p; //déclare une instance p de la classe Point. 31
  • 32. Utilisation des objets Pour accéder aux membres d'un objet on utilise une notation pointée (.): nom_instance.nom_du_membre Le membre pouvant être soit un attribut soit une méthode. 3232
  • 33. Accès aux attributs 33 L’accès aux valeurs des attributs d’une instance de nom nom_instance se fait comme pour accéder aux champs d’une structure : nom_instance.nom_attribut Exemple : La valeur de l’attribut hauteur d’une instance rect de la classe Rectangle sera référencée par l’expression : rect.hauteur 33
  • 34. Accès aux attributs 34 Exemple : #include <iostream> using namespace std; class Rectangle { double hauteur; double largeur; }; int main(){ Rectangle rect; rect.hauteur = 5.0; rect.largeur = 6.0; cout << "Hauteur : " << rect.hauteur << endl; cout << "Largeur : " << rect.largeur << endl; return 0; } 34
  • 35. Appel aux méthodes 35 L’appel aux méthodes définies pour une instance de nom nom_instance se fait à l’aide d’expressions de la forme : nom_instance.nom_methode(arg1,…) Exemples: rect.surface() //rect est une instance de la classe Rectangle tableau.size() 35
  • 36. Appel aux méthodes 36 Exemple: // ... class Rectangle { double hauteur; double largeur; double surface() const{ return hauteur * largeur; } }; int main(){ Rectangle rect; rect.hauteur = 5.0; rect.largeur = 6.0; cout << " Surface du rectangle = " << rect.surface() << endl; // ... } 36
  • 37. Accès aux attributs et méthodes 37 Chaque instance a ses propres attributs : aucun risque de confusion d’une instance à une autre. rect1.surface() : méthode surface de la classe Rectangle s’appliquant à rect1 rect1.surface() Rectangle::surface(&rect1) 37
  • 39. Encapsulation et interface 39 Tout ce qu’il n’est pas nécessaire de connaître à l’extérieur d’un objet devrait être dans le corps de l’objet et identifié par le mot clé private : class Rectangle { double surface() const; // Tout ce qui suit est privé (inaccessible depuis l'extérieur) private: double hauteur; double largeur; }; Attribut d’instance privée = inaccessible depuis l’extérieur de la classe. C’est également valable pour les méthodes. Si aucun droit d’accès n’est précisé, par défaut, tous les éléments d'une classe sont private. 39
  • 40. Encapsulation et interface 40 L’interface, qui est accessible de l’extérieur, se déclare avec le mot-clé public: class Rectangle { // Tout ce qui suit est public (accessible depuis l'extérieur) public: double surface() const; // Tout ce qui suit est privé (inaccessible depuis l'extérieur) private: // ... }; 40
  • 41. Encapsulation et interface 41 Best practice: dans la plupart des cas : Privé : Tous les attributs (encapsulation) La plupart des méthodes Public : Quelques méthodes bien choisies (interface) Règle d'or en POO: Encapsulation : tous les attributs d'une classe doivent toujours être privés. 41
  • 42. Encapsulation et interface 42 En règle générale, il est recommandé de masquer les attributs (champs) en les déclarant private et -seulement si nécessaire- de définir des méthodes publiques pour accéder à ces attributs. Ces méthodes sont appelées accesseurs/mutateurs ou getters/setters, généralement nommées getxxx() et setxxx(). Il est utile de proposer des méthodes pour accéder aux attributs: Lecture = getter Écriture = setter 42
  • 43. Encapsulation et interface 43 Getters/Setters Donc, si le programmeur le juge utile, il inclut les méthodes publiques nécessaires: Accesseurs (« méthodes get » ou « getters ») : Consultation (i.e. « prédicat ») Retour de la valeur d’une variable d’instance précise Exemple 1: double getHauteur() const { return hauteur; } double getLargeur() const { return largeur; } 43
  • 44. Accesseurs et manipulateurs 44 Getters/Setters Manipulateurs (« mutateurs » ou « méthodes set » ou « setters ») : Modification (i.e. « action ») Affectation de l’argument à une variable d’instance précise Exemple: void setHauteur(double h) { hauteur = h; } void setLargeur(double l) { largeur = l; } 44
  • 45. Accesseurs et manipulateurs 45 Getters/Setters Exemple: #include <iostream> using namespace std; class Rectangle{ public: double surface() const{ return hauteur * largeur; } double getHauteur() const{ return hauteur; } double getLargeur() const{ return largeur; } void setHauteur(double h){ hauteur = h; } void setLargeur(double l){ largeur = l; } private: double hauteur; double largeur; }; 45
  • 46. Accesseurs et manipulateurs 46 Exemple: (suite) int main(){ Rectangle rect; rect.setHauteur(3.0); rect.setLargeur(4.0); cout << "Hauteur : " << rect.getHauteur() << endl; cout << "Largeur : " << rect.getLargeur() << endl; return 0; } Output: Hauteur: 3 Largeur: 4 46
  • 47. Masquage/Shadowing 47 masquage = un identificateur « cache » un autre identificateur int main(){ int i(12); for (int i(0); i < MAX; ++i) { cout << i << endl; } cout << i << endl; return 0; } 47
  • 48. Masquage/Shadowing 48 Situation typique en POO : Un paramètre cache un attribut: //… class Rectangle{ public: void setHauteur(double hauteur) //paramètre hauteur { hauteur = hauteur; //ambiguïté } //… private: double hauteur; //attribut hauteur double largeur; }; 48
  • 49. Masquage et pointeur this 49 Si, dans une méthode, un attribut est masqué alors la valeur de l’attribut peut quand même être référencée à l’aide du mot réservé this. this est un pointeur sur l’instance courante this « adresse de l’objet courant » Syntaxe pour spécifier un attribut en cas d’ambiguïté : this -> nom_attribut Exemple : void setHauteur(double hauteur) { this->hauteur = hauteur; } L’utilisation de this est obligatoire dans les situations de masquage. 49
  • 50. Pointeur sur l’objet courant : this Exemple 1: this et variables d’instance Implicitement quand dans le corps d’une méthode un attribut est utilisé, c’est un attribut de l’objet courant. this essentiellement utilisé pour lever les ambiguïtés. 50 class Point{ double x; double y; void translater(int dx, int dy) { x += dx; y += dy; } double distance() { return sqrt(x*x+y*y); } void placerAuPoint(double x, double y1){ this->x = x; y = y1; } }; 50
  • 51. Pointeur sur l’objet courant : this Exemple 2: this et variables d’instance int main(){ Point a(1, 3) ; Point b(2, 5) ; Point c(1, 3) ; cout << "a et b : " << a.coincide(b) <<endl; cout << "a et c : " << a.coincide(c) <<endl; } 51 class Point{ private: int x, y; public Point(int abs, int ord){ x = abs ; y = ord ; } public: bool coincide(Point pt){ return ((pt.x == x) && (pt.y == y)) ; } }; 51
  • 52. Pointeur sur l’objet courant : this Exemple 2: this et variables d’instance 52 class Point{ private: int x, y; public Point(int abs, int ord){ x = abs ; y = ord ; } public: bool coincide(Point pt){ return ((pt.x == this->x) && (pt.y == this->y)) ; } }; int main(){ Point a(1, 3) ; Point b(2, 5) ; Point c(1, 3) ; cout << "a et b : " << a.coincide(b) <<endl; cout << "a et c : " << a.coincide(c) <<endl; } 52
  • 53. Classes et Objets 53 Exemple 1: #include <iostream> using namespace std; // définition de la classe class Rectangle{ // définition des méthodes public: double surface() const { return hauteur * largeur; } double getHauteur() const { return hauteur; } //Accesseur correspondant à hauteur. double getLargeur() const { return largeur; } //Accesseur correspondant à largeur. void setHauteur(double h) { hauteur = h; } //Manipulateur pour hauteur void setLargeur(double l) { largeur = l; } //Manipulateur pour hauteur // déclaration des attributs private: double hauteur; double largeur; }; 53
  • 54. Classes et Objets 54 Exemple 1 : (suite) //utilisation de la classe int main(){ Rectangle rect; double h, l; cout << "Saisir la hauteur du rectangle ? " << endl; cin >> h; rect.setHauteur(h); cout << "Saisir la largeur du rectangle ? " << endl; cin >> l; rect.setLargeur(l); cout << "Surface du rectangle = " << rect.surface() << endl; return 0; } 54
  • 55. 55 Exemple 1: Output: Saisir la hauteur du rectangle ? 6 Saisir la largeur du rectangle ? 7 Surface du rectangle = 42 Classes et Objets 55
  • 56. 56 Définitions externes à la classe Exemple 2: //Prototype de la classe class Rectangle{ public: // prototypes des méthodes double surface() const; // accesseurs double getHauteur() const; double getLargeur() const; // manipulateurs void setHauteur(double); void setLargeur(double); private: // déclaration des attributs double hauteur; double largeur; }; Classes et Objets 56
  • 57. 57 Définitions externes à la classe Exemple 2: (suite) //Définition de la classe double Rectangle::surface() const { return hauteur*largeur; } double Rectangle::getHauteur() const{ return hauteur; } double Rectangle::getLargeur() const{ return largeur; } void Rectangle::setHauteur(double h){ hauteur = h; } void Rectangle::setLargeur(double l){ largeur = l; } Classes et Objets 57
  • 58. Best practice (1/2) 58 Définitions externes à la classe //Prototype de la classe: class Rectangle{ public: // Prototypes des méthodes: double surface() const; // accesseurs double hauteur() const; double largeur() const; // manipulateurs void hauteur(double); void largeur(double); private: // Déclaration des attributs: double hauteur_; double largeur_; }; 58
  • 59. Best practice (2/2) 59 Définitions externes à la classe //Définition de la classe: double Rectangle::surface() const{ return hauteur_ * largeur_;} // définition des accesseurs double Rectangle::hauteur() const{ return hauteur_;} double Rectangle::largeur() const{ return largeur_;} // définition des manipulateurs void Rectangle::hauteur(double h){ hauteur_ = h; } void Rectangle::largeur(double l){ largeur_ = h; } 59
  • 60. Constructeurs 60 Initialisation des attributs Première solution : affecter individuellement une valeur à chaque attribut. Exemple: class Rectangle { public: double surface() const; double getHauteur() const; double getLargeur() const; void setHauteur(double h); void setLargeur(double l); private: double hauteur; double largeur; }; 60
  • 61. Constructeurs 61 Initialisation des attributs Première solution : affecter individuellement une valeur à chaque attribut. Exemple: (suite) Rectangle rect; double h, l; cout << "Quelle hauteur ? "; cin >> h; rect.setHauteur(h); cout << "Quelle largeur ? "; cin >> l; rect.setLargeur(l); 61
  • 62. Constructeurs 62 Initialisation des attributs Première solution : affecter individuellement une valeur à chaque attribut. Ceci est une mauvaise solution dans le cas général : elle implique que tous les attributs fassent partie de l’interface (public) ou soient assortis d’un manipulateur. casse l’encapsulation oblige le programmeur-utilisateur de la classe à initialiser explicitement tous les attributs. risque d’oubli 62
  • 63. Constructeurs 63 Initialisation des attributs Deuxième solution : définir une méthode dédiée à l’initialisation des attributs: class Rectangle{ public: void init(double h, double L){ hauteur = h; largeur = L; } ... private: double hauteur; double largeur; }; 63
  • 64. Constructeurs 64 Pour faire ces initialisations, il existe en C++ des méthodes particulières appelées constructeurs. Un constructeur est une méthode : invoquée automatiquement lors de la déclaration d’un objet; chargée d’effectuer toutes les opérations requises en « début de vie » de l’objet (dont l’initialisation des attributs). 64
  • 65. Constructeurs 65 Syntaxe de base NomDeLaClasse(liste_des_paramètres) { /* initialisation des attributs en utilisant liste_des_paramètres */ } Exemple: Rectangle(double h, double L){ hauteur = h; largeur = L; } 65
  • 66. Constructeurs 66 Un constructeur est une sorte de méthode qui sera invoquée lors de la création d’un objet de cette classe. Un constructeur doit porter le nom de la classe et ne doit pas comporter de type de retour (pas même void) dans sa déclaration. Un constructeur sera invoqué automatiquement à chaque fois qu’une instance est créée. Le but de constructeur est d’initialiser l’objet (notamment la valeur de ses champs). 66
  • 67. Constructeurs 67 Comme les autres méthodes : les constructeurs peuvent être surchargés on peut donner des valeurs par défaut à leurs paramètres Une classe peut donc avoir plusieurs constructeurs, pour peu que leur liste de paramètres soit différente. 67
  • 68. Constructeurs Initialisation par constructeur La création d'une instance (objet) est simple et répond à la syntaxe suivante : Syntaxe : NomDeLaClasse nom_instance(arg1, ..., argN); où arg1, ..., argN sont les valeurs des arguments du constructeur. Exemple : Rectangle r(18.0, 5.3); // invocation du constructeur à 2 paramètres 6868
  • 69. Instanciation des objets 69 Exemple (1/2): class Rectangle { public: Rectangle(double h, double L){ m_hauteur = h; m_largeur = L; } double surface() const{ return m_hauteur * m_largeur; } // accesseurs/modificateurs si nécessaire // ... private: double m_hauteur; double m_largeur; }; 69
  • 70. Instanciation des objets 70 Exemple (2/2): // ... int main(){ double h, l; cout << "Quelle hauteur ? "; cin >> h; cout << "Quelle largeur ? "; cin >> l; Rectangle rect1(h,l); // instanciation de l’objet rect de type Rectangle cout << "Surface = " << rect1.surface(); Rectangle rect2(4.0, 5.0); cout << "Surface = " << rect2.surface(); } 70
  • 71. Appel aux constructeurs des attributs 71 Un constructeur devrait normalement contenir une section d’appel aux constructeurs des attributs.... ...ainsi que l’initialisation des attributs de type de base. C’est ce qu’on appelle la « liste d’initialisation » du constructeur. Syntaxe générale: NomDeLaClasse(liste_des_paramètres) // liste d’initialisation : attribut1(...), // appel au constructeur de attribut1 ... , attributN(...) // appel au constructeur de attributN { // corps du constructeur contenant d’autres opérations } 71
  • 72. Liste d’initialisation 72 Cette section introduite par « : » est optionnelle mais recommandée. Par ailleurs : les attributs non-initialisés dans cette section: • prennent une valeur par défaut si ce sont des objets ; • restent indéfinis s’ils sont de type de base ; les attributs initialisés dans cette section peuvent être changés dans le corps du constructeur. 72
  • 73. Liste d’initialisation 73 Utilisation mixte de liste d'initialisation et affectations dans le corps du constructeur. Exemple: Rectangle(double h, double L) : m_hauteur(h) //initialisation { // largeur a une valeur indéfinie jusqu'ici m_largeur = 2.0 * L + h; // par exemple... // la valeur de largeur est définie à partir d'ici } 73
  • 74. Liste d’initialisation 74 Deux petites choses à retenir impérativement concernant les listes d'initialisation : Les attributs doivent apparaître dans leur ordre de déclaration. Les initialisations réalisées dans la liste d'initialisation sont effectuées avant les instructions situées dans le code du corps du constructeur. 74
  • 75. Construction des attributs 75 Que se passe-t-il si les attributs sont eux-mêmes des objets ? class RectangleColor{ private: Rectangle m_rectangle; Couleur m_couleur; //... }; Mauvaise solution : RectangleColor(double h, double L, Couleur c){ m_rectangle = Rectangle(h, L); m_couleur = c; } Il faut initialiser directement les attributs en faisant appel à leurs propres constructeurs ! 75
  • 76. Appel aux constructeurs des attributs 76 Exemple: class Rectangle{ Rectangle(double h, double L); // ... }; class RectangleColor { RectangleColor(double h, double L, Couleur c) : rectangle(h, L), couleur(c) {} private: Rectangle m_rectangle; Couleur m_couleur; }; 76
  • 77. Constructeur par défaut 77 Le constructeur par défaut est un constructeur qui n’a pas de paramètre ou dont tous les paramètres ont des valeurs par défaut. Exemple 1: // Constructeur par défaut avec liste d’initialisation Rectangle() : m_hauteur(2.0), m_largeur(3.0) {} // Constructeur par défaut sans liste d’initialisation Rectangle() { m_hauteur = 2.0; m_largeur = 3.0; } … int main(){ Rectangle rect1; // sans parenthèses } 77
  • 78. Constructeur par défaut 78 Autre façon de faire : regrouper 2 constructeurs en utilisant les valeurs par défaut des paramètres : Exemple 2: // DEUX constructeurs dont le constructeur par défaut Rectangle(double c = 1.0) : m_hauteur(c), m_largeur(2.0*c) {} // 3ème constructeur Rectangle(double h, double L) : m_hauteur(h), m_largeur(L) {} … int main(){ Rectangle rect1, rect2(5.5), rect3(2.5,3.5); … } 78
  • 79. Constructeur par défaut 79 Exemple 3: // TROIS constructeurs dont le constructeur par défaut Rectangle(double h = 1.0, double L = 1.0) : m_hauteur(h), m_largeur(L) {} 79
  • 80. Constructeur par défaut par défaut 80 Si aucun constructeur n’est spécifié, le compilateur génère automatiquement une version minimale du constructeur par défaut qui : appelle le constructeur par défaut des attributs objets. laisse non initialisés les attributs de type de base. Dès qu’au moins un constructeur a été spécifié, ce constructeur par défaut par défaut n’est plus fourni. Si donc on spécifie un constructeur sans spécifier de constructeur par défaut, on ne peut plus construire d’objet de cette classe sans les initialiser puisqu’il n’y a plus de constructeur par défaut. Depuis C++11, mais on peut le rajouter si on veut. 80
  • 85. Réactivation du constructeur par défaut par défaut 85 Dès qu’au moins un constructeur a été spécifié, ce constructeur par défaut par défaut n’est plus fourni. C’est très bien si c’est vraiment ce que l’on veut (c’est-à-dire forcer les utilisateurs de la classe à utiliser nos constructeurs). Mais si l’on veut quand même avoir le constructeur par défaut par défaut, depuis C++11 on peut le réactiver en écrivant dans la définition de la classe : NomDeLaClasse() = default; 85
  • 86. Réactivation du constructeur par défaut par défaut 86 Exemple: class Rectangle { public: Rectangle() = default; //réactivation du constructeur par défaut par défaut // 2ème constructeur Rectangle(double h, double L) : h_hauteur(h), m_largeur(L) {} // Méthode d’instance double surface() const { return m_hauteur * m_largeur; } // Variables d’instance private: double m_hauteur; double m_largeur; }; 86
  • 87. Méthodes default et delete depuis C++11 87 Ce que l’on a fait précédemment (= default) pour le constructeur par défaut se généralise : à toute méthode (pour laquelle cela est pertinent) à la suppression de méthode, via la syntaxe « = delete » Exemple: class DemoClasse{ public: double f(double x) { ... } double f(int) = delete; }; 87
  • 88. Chaînage des constructeurs 88 C++11 autorise les constructeurs d’une classe à appeler n’importe quel autre constructeur de cette même classe. Dans les classes définissant plusieurs constructeurs, un constructeur peut invoquer un autre constructeur de cette classe. 88
  • 89. Chaînage des constructeurs 89 Exemple: class Rectangle{ public: Rectangle(double h, double L) : m_hauteur(h), m_largeur(L) {} Rectangle() : Rectangle(0.0, 0.0) {} double surface() const { return m_hauteur * m_largeur; } private: double m_hauteur; double m_largeur; }; 89
  • 90. Initialisation par défaut des attributs 90 C++11 permet de donner directement une valeur par défaut aux attributs. Si le constructeur appelé ne modifie pas la valeur de cet attribut, ce dernier aura alors la valeur indiquée. Exemple: class Rectangle { // ... private: double hauteur = 0.0; double largeur = 0.0; // ... }; 90
  • 91. Constructeur de copie 91 C++ offre un moyen de créer la copie d’une instance en utilisant le constructeur de copie lors de l’initialisation: Rectangle r1(11.3, 12.5); Rectangle r2(r1); // constructeur de copie: création de l'objet r2 // et son initialisation avec les données de r1. Ou Rectangle r2 = r1; // constructeur de copie: création de l'objet r2 // et son initialisation avec les données de r1. Ou Rectangle* r2 = new Rectangle(r1); // constructeur de copie: création de l'objet // dynamique r2 et son initialisation avec les données de r1. r1 et r2 sont deux instances distinctes mais ayant des mêmes valeurs pour leurs attributs. 91
  • 92. Constructeur de copie 92 Le constructeur de copie est appelé dans trois situations: 1. Lors de l’initialisation d’un objet: Création d'un nouvel objet initialisé comme étant une copie d'un objet existant. 2. Lors d’un passage de paramètres par valeur : double maFonction(Rectangle r); //prototype de maFonction ... x = maFonction(r1); //appel de maFonction Copie de r1 dans r donc invocation du constructeur de copie. 3. Lors de retour de fonction par valeur Rectangle g() { Rectangle rect; … return rect; // retour par valeur de rect } 92
  • 93. Constructeur de copie 93 Le constructeur de copie permet d’initialiser une instance en copiant les attributs d’une autre instance du même type. Deux syntaxes possibles : NomDeLaClasse(NomDeLaClasse const& autre) { ... } ou NomDeLaClasse(NomDeLaClasse& autre) { ... } Exemple: Rectangle(Rectangle const& autre) : m_hauteur(autre.m_hauteur), m_largeur(autre.m_largeur) {} 93
  • 94. Constructeur de copie 94 Le but de ce type de constructeur est d'initialiser un objet lors de son instanciation à partir d'un autre objet. Toute classe dispose d'un constructeur de copie par défaut généré automatiquement par le compilateur s’il n’est pas explicitement défini (constructeur de copie par défaut). Le seul but est de recopier les attributs de l'objet un à un dans attributs de l'objet à instancier (si l’attribut est un objet le constructeur de cet objet est invoqué). Donc, le rôle du constructeur de copie est de copier la valeur de tous les attributs du premier objet dans le second. 94
  • 95. Constructeur de copie 95 Ce constructeur se contente d'effectuer une copie de chacun des membres. On retrouve là une situation analogue à celle qui est mise en place (par défaut) lors d'une affectation entre objets de même type. Elle posera donc les mêmes problèmes pour les objets contenant des pointeurs sur des emplacements dynamiques. On aura simplement affaire à une "copie superficielle", c'est-à-dire que seules les valeurs des pointeurs seront recopiées, les emplacements pointés ne le seront pas. copie de surface ou copie superficielle 95
  • 96. Constructeur de copie 96 Cette copie de surface suffit dans la plupart des cas. Cependant, il est parfois nécessaire de redéfinir le constructeur de copie, en particulier lorsque certains attributs sont des pointeurs. Il faut alors allouer une nouvelle zone mémoire, on parle alors de copie profonde (allocation puis recopie). 96
  • 97. Constructeur de copie 97 La règle de trois : Si vous touchez à l’un des trois parmi: constructeur de copie, destructeur, opérateur d’affectation (operator=), alors pensez aux deux autres. 97
  • 98. Constructeur de copie 98 Remarque: Il faut faire attention entre copie et affectation. Rectangle rect1, rect2; // création de deux objets rect1 et rect2. rect1 = rect2; // pas de copie mais uniquement l'affectation // car l’objet est déjà créé. 98
  • 99. Suppression du constructeur de copie 99 Depuis C++11, si l’on souhaite interdire la copie, il suffit de supprimer le constructeur de copie par défaut avec la commande « = delete ». Exemple: class Incopiable { /*... */ Incopiable(Incopiable const&) = delete; }; 99
  • 100. Destructeur 100 Si l’initialisation des attributs d’une instance implique la mobilisation de ressources : fichiers, périphériques, portions de mémoire (pointeurs), etc. Il est alors important de libérer ces ressources après usage. Comme pour l’initialisation, l’invocation explicite de méthodes de libération n’est pas satisfaisante (fastidieuse, source d’erreur, affaiblissement de l’encapsulation). 100
  • 101. Destructeur 101 C++ offre une méthode appelée destructeur invoquée automatiquement en fin de vie de l’instance. Il s'exécute à la fin du programme ou d'un bloc où des objets locaux ont été définis. Un destructeur a pour rôle de libérer la mémoire. Il est appelé automatiquement lorsque l’objet n’est plus utilisé, fermeture d’accolade, … 101
  • 102. Destructeur 102 La syntaxe de déclaration d’un destructeur pour une classe NomDeLaClasse est : ˜NomDeLaClasse() { // opérations (de libération) } Le destructeur d’une classe est une méthode sans paramètre donc pas de surcharge possible Son nom est celui de la classe, précédé du signe ˜ (tilda). Si le destructeur n’est pas défini explicitement par le programmeur, le compilateur en génère automatiquement une version minimale. 102
  • 103. Constructeur et destructeur 103 Exemple 1: Supposons que l’on souhaite compter le nombre d’instances d’une classe actives à un moment donné dans un programme. int main(){ // compteur = 0 Rectangle r1; // compteur = 1 { Rectangle r2; // compteur = 2 // ... } // compteur = 1 return 0; } // compteur = 0 103 Appel du constructeur Appel du destructeur pour r2 et r1
  • 104. Constructeur et destructeur 104 Exemple 1: Utilisons comme compteur une variable globale de type entier : Le constructeur incrémente le compteur long compteur(0); class Rectangle{ //... Rectangle(): m_hauteur(0.0), m_largeur(0.0) //constructeur { ++compteur; } // ... 104
  • 105. Constructeur et destructeur 105 Exemple 1: On est obligé ici de définir explicitement le destructeur int main(){ // compteur = 0 Rectangle r1; // compteur = 1 { Rectangle r2; // compteur = 2 // ... } // compteur = 2 return 0; } // compteur = 2 105
  • 106. Constructeur et destructeur 106 Exemple 1: Le constructeur incrémente le compteur Le destructeur le décrémente long compteur(0); class Rectangle { //... Rectangle(): m_hauteur(0.0), m_largeur(0.0) {//constructeur ++compteur; } ~Rectangle() {// destructeur --compteur; } // ... 106
  • 107. Constructeur et destructeur 107 Exemple 1: int main(){ // compteur = 0 Rectangle r1; // compteur = 1 { Rectangle r2; // compteur = 2 // ... } // compteur = 1 return 0; } // compteur = 0 107
  • 108. Constructeur et destructeur 108 Exemple 1: Que se passe-il si l’on souhaite utiliser la copie d’objet ? int main(){ // compteur = 0 Rectangle r1; // compteur = 1 { Rectangle r2; // compteur = 2 Rectangle r3(r2); // compteur = ?? } // compteur = return 0; } // compteur = 108 Appel du constructeur Appel du constructeur de copie Appel du destructeur pour r2, r3 et r1
  • 109. Constructeur et destructeur 109 Exemple 1: La copie d’un rectangle échappe au compteur d’instances car il n’y a pas de définition explicite du constructeur de copie. Il faudrait donc encore ajouter au code précédent, la définition explicite du constructeur de copie : Rectangle(Rectangle const& r) : m_hauteur(r.m_hauteur), m_largeur(r.m_largeur) { ++compteur; } Règle générale : Si on doit toucher à l’un des trois parmi destructeur, constructeur de copie et opérateur d’affectation (=), alors on doit certainement également toucher aux deux autres (ou alors au moins se poser la question !). 109
  • 110. Constructeur et destructeur 110 Exemple 2: class Chaine{ // Implémente une chaîne de caractères. private: char* s; // Le pointeur sur la chaîne de caractères. public: Chaine(); // Le constructeur par défaut. Chaine(int); // Le constructeur. Il n'a pas de type. ~Chaine(); // Le destructeur. }; Chaine::Chaine(){ s = nullptr; } // La chaîne est initialisée avec le pointeur nullptr. Chaine::Chaine(int Taille){ s = new char[Taille+1]; // Alloue de la mémoire pour la chaîne. s[0]='0'; // Initialise la chaîne à "". } Chaine::~Chaine(){ if (s!=nullptr) delete[] s; // Restitue la mémoire utilisée si nécessaire. } 110
  • 111. Création des instances dynamiques 111 Dans le cas d'une instance dynamique, il faut commencer par déclarer un pointeur, puis appeler l’opérateur new suivi du nom du constructeur avec ses paramètres. Syntaxe: NomDeLaClasse *nom_instance; nom_instance = new NomDeLaClasse(arg1,..., argN); Ou directement NomDeLaClasse *nom_instance = new NomDeLaClasse(arg1,...,argN); où arg1, ..., argN sont les valeurs des arguments du constructeur. 111
  • 112. Création des instances dynamiques 112 Exemple: Rectangle *r1(nullptr), *r2(nullptr); // Déclaration d'un pointeur sur un objet de type Rectangle r1 = new Rectangle(4.0, 5.0);// Instanciation de l'objet dynamique avec arguments explicites r2 = new Rectangle; // Instanciation de l'objet dynamique avec arguments par défaut delete r1; // On n'oublie pas de rendre la mémoire ! delete r2; // Idem 112
  • 113. Création des instances dynamiques 113 Appel de méthodes d’instance La syntaxe d'appel d'un membre d’instance par un objet dynamique est: nom_instance -> nom_membre Il faut juste changer le "." par une flèche "->". Exemple: Rectangle *rect1 = new Rectangle(2.5, 3.5); Rectangle *rect2 = new Rectangle; cout << "Hauteur = "<< rect1-> m_hauteur << endl; cout << "Largeur = "<< rect1-> m_largeur << endl; cout << "Surface du rectangle 1 = "<< rect1-> surface() << endl; cout << "Surface du rectangle 2 = "<< (*rect2).surface() << endl; 113
  • 114. Envoi de messages Pour "demander" à un objet d'effectuer une opération (exécuter l'une de ses méthodes) il faut lui envoyer un message. Un message est composé de trois parties: un identificateur permettant de désigner l'objet à qui le message est envoyé. le nom de la méthode à exécuter (cette méthode doit bien entendu être définie dans la classe de l'objet). les éventuels paramètres de la méthode. 114114
  • 115. Envoi de messages Envoi de message similaire à un appel de fonction les instructions définies dans la méthode sont exécutées (elles s’appliquent sur les attributs de l’objet récepteur du message). puis le contrôle est retourné au programme appelant. Les objets communiquent entre eux par échange de messages. 115115
  • 116. Envoi de messages Syntaxe : nom_instance.nomDeMethode(<liste_paramètres_effectifs>) Si la méthode ne possède pas de paramètres, la liste est vide, mais comme en langage C les parenthèses demeurent. class Point{ private: double x{0.0}; double y{0.0}; public: void translater(double dx, double dy){ x += dx; y += dy; } double distance(){ return sqrt(x*x+y*y); } }; // Point Point p1, p2; p1.translater(10.0,10.0); cout << “Distance de p2 à l’origine : “<< endl; cout << p2.distance(); 116116
  • 117. Mise en œuvre d’un programme comportant plusieurs fichiers Un fichier entête Classe.h qui définit la classe (appelé interface) va contenir le prototype de la classe, c.à.d., la déclaration de la classe avec les attributs et les prototypes des méthodes. Un fichier source Classe.cpp contient la définition de la classe, c’est là qu'on va implémenter les méthodes membres. L’implémentation des méthodes peut se faire dans l’entête. Un fichier source (.cpp) contient la fonction main(). Exemple: Point.h: contient le prototype de la classe (interface); Point.cpp: contient la définition de la classe, c.à.d., l'implémentation des méthodes membres de la classe. TestPoint.cpp: contient la fonction main(). 117117
  • 118. Mise en œuvre d’un programme comportant plusieurs fichiers Exemple (1/2): Point.h Point.cpp #ifndef POINT_H_INCLUDED #define POINT_H_INCLUDED class Point { public: void translater(double dx, double dy); double distance(); void afficher(); private: double x; double y; }; #endif // POINT_H_INCLUDED #include "Point.h“ #include<iostream> #include<cmath> using namespace std; void Point::translater(double dx, double dy){ x += dx; y += dy; } double Point::distance() { return sqrt(x*x+y*y); } void Point::afficher(){ cout << "(" << x << ", "<< y << ") " << endl; } 118118
  • 119. Mise en œuvre d’un programme comportant plusieurs fichiers Exemple (2/2): TestPoint.cpp #include "Point.h“ #include<iostream> using namespace std; int main(){ Point p1, p2; p1.translater(10.0,10.0); p1.afficher(); cout << “distance de p2 à l’origine“ << p1.distance() << endl; } 119119
  • 120. Mise en œuvre d’un programme comportant plusieurs classes Plusieurs classes dans un même fichier source TestPoint.cpp 120120 #include<iostream> #include<cmath> using namespace std; class Point { public: void translater(double dx, double dy); double distance(); void afficher(); private: double x; double y;}; void Point::translater(double dx, double dy){ x += dx; y += dy;} double Point::distance() { return sqrt(x*x+y*y);} void Point::afficher(){ cout << "(" << x << ", "<< y << ") " << endl; } int main(){ Point p1, p2; p1.translater(10.0,10.0); p1.afficher(); cout << “distance de p2 à l’origine“ << p1.distance() << endl; }
  • 121. Surcharge des méthodes Surcharge (overloading) pas limitée aux constructeurs, elle est possible pour n'importe quelle méthode. Possible de définir des méthodes possédant le même nom mais dont les arguments diffèrent. Lorsque qu'une méthode surchargée est invoquée le compilateur sélectionne automatiquement la méthode dont le nombre et le type des arguments correspondent au nombre et au type des paramètres passés dans l'appel de la méthode. Des méthodes surchargées peuvent avoir des types de retour identiques mais à condition qu'elles aient des arguments différents. 121121
  • 122. Surcharge des méthodes Exemple class Point{ private: double x; double y; public: // constructeur Point(double x, double y): x(x), y(y){} // destructeur ~Point(){cout<< “Destructeur“ <<endl;} // méthodes double distance() { return sqrt(x*x+y*y); } ... } double distance(Point p){ return sqrt((x - p.x)*(x - p.x) + (y - p.y)*(y - p.y)); } … }; Point p1(10,10); Point p2(1.5,1.4); cout<<“Distance =”<<p1.distance(); cout<<“Distance entre p1 et p2 =”; cout<<p1.distance(p2); 122122
  • 123. Variables et méthodes d’instance 123123
  • 124. Variables et méthodes d’instance Variables d’instance ou attributs d’instance Déclarées par accès: type nomDeVariableInstance Référencées par Objet.nomDeVariableInstance Une copie pour chaque instance (objet) Chaque objet possède ses propres membres donnée. Méthodes d’instance Déclarées par accès: TypeRetour nomDeLaMethodeInstance(…) Référencées par objet.nomDeLaMethodeInstance(…) 124124
  • 125. Membres de classe: static 125125
  • 126. Membres de classe: static 126126
  • 127. Variables de classe: static Il peut être utile de définir pour une classe des attributs indépendamment des instances : nombre de Points crées Les variables statiques sont enregistrées au niveau de la classe et elles peuvent être accédées et manipulées par toutes les instances (objets) de cette classe. Utilisation des variables de classe comparables aux “variables globales“ partagées par toutes les instances. Variables dont il n’existe qu’un seul exemplaire associé à sa classe de définition. Variables existent indépendamment du nombre d’instances de la classe qui ont été créés. Variables utilisables même si aucune instance de la classe n’existe. 127127
  • 128. Variables de classe Déclarées par accès: static type nomDeLaVariableClasse Référencées par NomDeLaClasse::nomDeLaVariableClasse Une seule copie pour toutes les instances Les variables statiques appartiennent à la classe et non aux objets créés à partir de la classe. Un attribut static, bien qu'il soit accessible de l'extérieur, peut très bien être déclaré private ou protected N. B: Variables de classe = Variables statiques = Attributs de classe = Attributs statiques. 128128
  • 129. Variables de classe Initialisation d’une variable statique Il faut initialiser l'attribut statique dans l'espace global, c'est-à-dire en dehors de toute classe ou fonction, en dehors du main() notamment. Un attribut déclaré comme statique se comporte comme une variable globale, c'est-à-dire une variable accessible partout dans le code. Un attribut de classe doit être initialisé explicitement à l’extérieur de la classe. Les attributs de classe sont très pratiques lorsque différents objets d’une classe doivent accéder à une même information. Ils permettent notamment d’éviter que cette information soit dupliquée au niveau de chaque objet. Exemple: // Initialiser l'attribut en dehors de toute fonction ou classe (espace global) int MaClasse::attributStatique = 10; 129129
  • 130. Variables de classe Exemple: class Point { private: double x; double y; static int nbPointCrees; public: Point(double x, double y){ this->x = x; this->y=y; } … }; //initialisation de l’attribut statique int Point::nbPointCrees=0; 130130
  • 132. Méthodes de classe Usage Ce sont des méthodes qui ne s'intéressent pas à un objet particulier Utiles pour des calculs intermédiaires internes à une classe Utiles également pour retourner la valeur d'une variable de classe Elles sont déclarées comme les méthodes d'instances, mais avec le mot- clé static: public: static double vitesseMaxToleree(); Elles sont définies hors de la classe sans static: double Voiture::vitesseMaxToleree() { return vitesseMaxAutorisee*1.10; } Pour y accéder, il faut utiliser non pas un identificateur d'objet mais le nom de la classe et de l’opérateur de résolution de portée « :: » (idem pour les variables de classe): Voiture::vitesseMaxToleree(); // Voiture une classe 132132
  • 133. Méthodes de classe Exemple (1/2): 133 #include<iostream> using namespace std; class Point { private: double x; double y; static int compteur; public: Point(double x, double y); ~Point(); static int nombreInstances(); //Renvoie le nombre d'objets créés }; //initialisation de l’attribut statique int Point::compteur=0; //Quand on crée un point, on ajoute 1 au compteur Point:: Point(double x, double y):x(x), y(y){++compteur;} //Et on enlève 1 au compteur lors de la destruction Point:: ~Point(){--compteur;} int Point::nombreInstances(){ return compteur; //On renvoie simplement la valeur du compteur } 133
  • 134. Méthodes de classe Exemple (2/2): Output: Il y a actuellement 2 points construits. 134 int main() { //On crée deux points Point p1(1.0, 2.0); Point p2(3.0, 4.0); //Et on consulte notre compteur cout << "Il y a actuellement " << Point::nombreInstances() << " points construits." << endl; return 0; } 134
  • 135. 135135 Propriétés des fonctions membres Les fonctions membres (y compris les constructeurs) peuvent: être surdéfinies, avoir des arguments par défaut, être public ou private, être inline. Les fonctions membres ont accès à tous les membres de tous les objets de la classe.
  • 136. Surdéfinition des fonctions membre C++ autorise la surdéfinition (ou surcharge) des fonctions membres y compris les constructeurs (mais pas aux destructeurs); Exemple (1/2): class Point{ private : double x, y; public : Point(); // constructeur 1 sans arguments Point(double); // constructeur 2 avec un argument Point(double, double); // constructeur 3 avec deux arguments void afficher(); // fonction afficher() sans arguments void afficher(char*); // fonction affiche() avec un argument }; Propriétés des fonctions membres 136
  • 137. Propriétés des fonctions membres Surdéfinition des fonctions membre Exemple (2/2): Point::Point(): x(0), y(0){} Point::Point(double abs) : x(abs), y(abs) {} Point::Point(double abs, double ord): x(abs), y(ord) {} void Point::afficher(){ cout << "(" << x << ", " << y << ") " << endl; } void Point::afficher(char *msg) { cout << msg << endl; afficher(); } 137
  • 138. Arguments par défaut Tout comme une fonction C++ classique, il est possible de définir des arguments par défaut. Ceux-ci permettent à l'utilisateur de ne pas renseigner certains paramètres. On peut modifier notre classe Point pour qu’elle ne possède qu’une seule fonction affiche à un seul argument de type chaîne. void afficher(char* = " " ); // fonction afficher(): un argument par défaut void Point::afficher(char *msg) { cout << msg << "(" << x << "," <<y << ")" <<endl; } Point A; A.afficher(); //affichage: (0, 0) Point B(1.5, 2.5); B.afficher(" Point B"); // affichage : Point B(1.5, 2.5) Propriétés des fonctions membres 138
  • 139. Les fonctions membre en ligne 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 le qualificatif inline n’a pas à être utilisé (implicite). 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, le qualificatif inline doit apparaître à la fois devant la déclaration et devant l’en tête (explicite). Propriétés des fonctions membres 139
  • 140. Les fonctions membre en ligne Exemple: class Rectangle{ … public : inline Rectangle(); … }; // Fin de la classe inline Rectangle(): hauteur(0.0), largeur(0.0) { } Propriétés des fonctions membres 140
  • 141. Typologie des méthodes d’une classe 141 Parmi les différentes méthodes que comporte une classe, on a souvent tendance à distinguer : Les constructeurs et les destructeurs; Les méthodes d’accès (en anglais accessor/getter), en bon français accesseurs, qui fournissent des informations relatives à l’état d’un objet, c’est-à-dire aux valeurs de certains de ses champs (généralement privés), sans les modifier ; Les méthodes d’altération (en anglais mutator/setter), en bon français manipulateur ou mutateur, qui modifient l’état d’un objet, donc les valeurs de certains de ses champs. 141