03 Classes Et Objets
03 Classes Et Objets
03 Classes Et Objets
A. U: 2021/2022
Classes et objets
en C++
2
Points principaux
1. Structures en C++
2. Notion de classe
3. Affectation d’objets
4. Notions de constructeur et de destructeur
– Constructeur, initialisation et destructeur
5. Membres données statiques
6. Exploitation d’une classe
7. Classes en général
8. Propriétés des fonctions membres
3
Structures en C++ : rappel
Structures simples
• Une structure contient une ou plusieurs variables groupées sous le
même nom pour être traitées comme une seule entité.
#include <iostream>
using namespace std ;
void point :: initialise (int abs, int ord){
x = abs ; y = ord ;
}
void point :: deplace (int dx, int dy){
x += dx ; y += dy ;
}
void point :: affiche (){
cout << "Je suis en " << x << " " << y << "\n" ;
}
8
Utilisation d'une structure comportant des
fonctions membres
• On peut déclarer autant de structures de ce type que nous le souhaitons.
Par exemple :
point a, b ;
Déclare deux structures nommées a et b, chacune possédant des
membres x et y et disposant des trois méthodes initialise, deplace et
affiche.
– a.x = 5 ;
– a.initialise (5,2) ;
• a.x = 5 ; a.y = 2 ;
• Puisqu'il n’y a pas d'encapsulation des données, cette démarche est
purement artificielle. Alors que d'un point de vue conceptuel, elle permet
de mieux envisager la programmation, ainsi de préparer à la notion de
classe.
• En P.O.O., on dit également que a.initialise (5, 2) permet l'envoi du
message (informations 5 et 2) à l'objet a.
9
Exemple complet
#include <iostream>
using namespace std ;
/* ------------ Déclaration du type point ------------- */
struct point{ /* déclaration "classique" des données */
int x , y ;
/* déclaration des fonctions membres (méthodes) */
void initialise (int, int) ;
void deplace (int, int) ;
void affiche () ;
};
/* ----- Définition des fonctions membres du type point ---- */
void point::initialise (int abs, int ord) { x = abs ; y = ord ; }
void point::deplace (int dx, int dy) { x += dx ; y += dy ; }
void point::affiche () { cout << "Je suis en " << x << " " << y << "\n" ; }
main()
{ point a, b ;
a.initialise (5, 2) ; a.affiche () ; Je suis en 5 2
a.deplace (-2, 4) ; a.affiche () ; Je suis en 3 6
b.initialise (1,-1) ; b.affiche () ; Je suis en 1 -1
} 10
Exemple complet (suite)
• Une fonction membre ne peut pas être appelée comme une fonction
ordinaire. Ex. initialise (3,1)//erreur
Il faut avoir un point : point p; p.initialise(3,1);
• Dans la déclaration d'une structure, il est permis (mais généralement peu
conseillé) d'introduire les données et les fonctions dans un ordre quelconque
(on a systématiquement placé les données avant les fonctions).
• Dans notre exemple de programme complet, on a introduit :
– La déclaration du type point,
– La définition des fonctions membres,
– La fonction (main) utilisant le type point.
• Mais, bien entendu, il serait possible de compiler séparément le type point;
c’est d’ailleurs ainsi que l’on pourra "réutiliser" ce composant. 11
Notion de classe
• La notion de classe en C++ est une extension de la notion de structure du
C avec les concepts fondamentaux suivants :
– Couplage des données et des fonctions (appelées méthodes ou
fonctions membres);
– Encapsulation des données, statut privé:
Ces données privées ne peuvent être manipulées que par les méthodes de
la classe.
Résistance
Accesseurs et Modificateurs
• Les Accesseurs
• Les Modificateurs
15
Notion de classe (suite)
Quelques règles de programmation
1. Définir les classes, inclure les librairies etc. dans un fichier d’extension .h
La classe est définie dans un fichier d ’extension .h ou .hxx
Exemple: point.h
2. Définir le corps des méthodes (fonctions membres), la fonction main etc. dans un
fichier d’extension .cpp, cxx, .C, .cc, (incluant le fichier .h)
Les fonctions sont rattachées à la classe par l ’opérateur de portée ::
Exemple:
void point::initialise( int a ,int b)
3. Compiler régulièrement
Penser à utiliser les commentaires et les coûts.
16
Notion de classe (suite)
Exemple d’une classe
#include <iostream>
using namespace std ;
/* ------------ Déclaration de la classe point -------------point.h */
class point
{ /* déclaration des membres privés */
private : /* facultatif */
int x ;
int y ;
/* déclaration des membres publics */
public :
void initialise (int, int) ;
void deplace (int, int) ;
void affiche () ;
};
17
Notion de classe (suite)
Exemple d’une classe (suite)
/* ----- Définition des fonctions membres de la classe point ---- point.cpp*/
void point::initialise (int abs, int ord) {
x = abs ; y = ord ;
}
void point::deplace (int dx, int dy) {
x = x + dx ; y = y + dy ;
}
void point::affiche () {
cout << "Je suis en " << x << " " << y << "\n" ;
}
/* -------- Utilisation de la classe point -------- */
main() {
point a, b ;
a.initialise (5, 2) ; a.affiche () ;
a.deplace (-2, 4) ; a.affiche () ;
b.initialise (1,-1) ; b.affiche () ;
18
}
Notion de classe (suite)
• Dans le jargon de la P.O.O., on dit que a et b sont des instances de la classe point,
ou encore que ce sont des objets de type point ; c’est généralement ce dernier terme
que l’on va utiliser.
• Dans notre exemple, tous les membres données de point sont privés, ce qui
correspond à une encapsulation complète des données. Ainsi, une tentative
d'utilisation directe (ici au sein de la fonction main) du membre a :
a.x = 5; //erreur de compilation
• Bien entendu, cette instruction serait acceptée si l’on avait fait de x un membre
public).
• En général, on cherchera à respecter le principe d'encapsulation des données, et donc
à prévoir des fonctions membres appropriées pour y accéder.
• Dans notre exemple, toutes les fonctions membres étaient publiques. Il est tout à fait
possible d'en rendre certaines privées. Dans ce cas, de telles fonctions ne seront
plus accessibles de l’"extérieur" de la classe. Elles ne pourront être appelées que par
d'autres fonctions membres.
19
Notion de classe (suite)
21
Affectation d’objet
• Il est donc nécessaire de faire appel à une fonction membre pour attribuer
des valeurs aux données d'un objet. C'est ce que nous avons fait pour notre
type point avec la fonction initialise.
• Les objets suivent les règles habituelles concernant leur initialisation par
défaut :
Personne.h Personne.cpp
class Personne{
public : Personne:: Personne(){
// Instructions du constructeur par défaut
Personne(); identifiant = 1;
Personne(int id); }
private : Personne:: Personne(int id)
int identifiant; {
}; identifiant = id;
}
main.cpp
int main(int argc, char argv){
Personne p1; //Appel de Personne()
Personne p2(2); //Appel de Personne(int)
Personne *pp3, *pp4;
pp3 = new Personne(); //Appel de Personne()
pp4 = new Personne(4); //Appel de Personne(int)
return 0;
} 25
Constructeur par recopie
Personne.h Personne.cpp
class Personne Code : Constructeur par recopie
{
public : Personne::Personne(const Personne& personne)
... {
Personne(const Personne&); identifiant = personne. identifiant;
... }
};
26
Questions
• Soit la définition de la classe T
class T{
public :
T();
T( int );
T(double);
T(const T&);
};
Quel constructeur est appelé ?
– T t1; //T :: T();
– T t2(12); //T :: T( int );
– T t3(t1); //T :: T(const T&);
– T t4() ; //Ce n'est pas une instruction c' est un prototype
27
Destructeur
• Appelé à chaque fois qu'une instance de la classe est détruite
(automatiquement);
– Rôle : libérer la mémoire;
– De même nom que la classe mais précédé d'un tilde (~);
– Définition de la désinitialisation d'une instance de la classe;
– Appelé implicitement à toute disparition d'instance de la classe:
fermeture d'accolade }
– Méthode non typée et sans paramètre;
– Ne pouvant pas être surchargé;
– Par défaut un destructeur vide est implémenté;
– Il est défini avec une visibilité public;
– delete fait appel au destructeur alors que free ne le fait pas
• Peut être appelé explicitement: delete a; delete[] a;
• a.~MaClasse(); //Pas recommandé…
28
Exemple de destructeur
Personne.cpp
Personne.h Personne:: Personne(int id){
class Personne{ identifiant = id;
Allocationdyna = new int();
private : }
int Personne:: ~Personne(){
*allocationdyna; delete allocationdyna ;
int identifiant; }…
public :
Personne(int id); main.cpp
Personne(); int main(int argc, char argv)
~Personne(); {
}; Personne p1, *pp3; //Appel de Personne()
pp3 = new Personne();
if(p1){
Personne p2;
} //Appel de ~Personne()
delete pp3; //Appel de ~Personne()
} //Appel de ~ Personne() 29
Exemple de destructeur (suite)
class Tableau {
int *t; int main()
public: { Tableau T;
Tableau(int s = 10) { // …
//constructeur
t = new int[s]; return 0;
} } /*Destructeur ~Tableau()
appelé automatiquement
~Tableau() { // destructeur en fin de portée*/
delete [] t;
}
};
30
Exemple de destructeur (suite)
// Exemple de constructeur effectuant une allocation repris de [Delannoy]
class TableauDEntiers {
int nbElements;
int * pointeurSurTableau;
public:
TableauDEntiers(int, int); // Constructeur
~ TableauDEntiers(); // Destructeur
…}
// Constructeur allouant dynamiquement de la mémoire pour nb entiers
TableauDEntiers::TableauDEntiers (int nb, int max) {
pointeurSurTableau = new int [nbElements=nb] ;
for (int i=0; i<nb; i++) // nb entiers tirés au hasard
pointeurSurTableau[i]= double(rand())/ RAND_MAX*max;
} // rand() fournit un entier entre 0 et RAND_MAX (cte varie suivant les compilat.
<32767)
TableauDEntiers::~TableauDEntiers () {
delete [] pointeurSurTableau ; // désallocation de la mémoire
31
}
#include <iostream>
Exemple : classe point
using namespace std ;
/* ------------ Déclaration de la classe point ------------- */
class point{
/* déclaration des membres privés */
int x, y ;
/* déclaration des membres publics */
public :
point (int, int) ; // constructeur
~point () ; // destructeur
void deplace (int, int) ;
void affiche () ;
};
/* ----- Définition des fonctions membre de la classe point ---- */
point::point (int abs, int ord) {x = abs ; y = ord ;}
point::~point () {cout << " Destructeur …\n" ;}
void point::deplace (int dx, int dy) { x = x + dx ; y = y + dy ;}
void point::affiche () { cout << "Je suis en " << x << " " << y << "\n" ;}
/* -------- Utilisation de la classe point -------- */
main() {
point a(5,2) ; a.affiche () ; Je suis en 5 2
a.deplace (-2, 4) ; a.affiche () ; Je suis en 3 6
point b(1,-1) ; b.affiche () ; Je suis en 1 -1
32
} //appel des destructeurs Destructeur …
Appel des constructeurs et des destructeurs
class point{
/* déclaration des membres privés */
int x, y ;
/* déclaration des membres publics */
public :
point (int, int) ; // constructeur1
~point () ; // destructeur
void deplace (int, int) ;
void affiche () ;
};
main() {
point a ; // Erreur: le constructeur attend deux arguments
point b (3) ; // Erreur (même raison)
}
33
Appel des constructeurs et des destructeurs (suite)
class point{
/* déclaration des membres privés */
int x, y ;
/* déclaration des membres publics */
public :
point (int, int) ; // constructeur1
point () ; // constructeur2
~point () ; // destructeur
void deplace (int, int) ;
void affiche () ;
};
main() {
point a ; // ou point a() OK :
point b(1, 7) ; // OK
point b (3) ; // Erreur : le constructeur attend deux arguments
} //appel des destructeurs
RQ: Une déclaration telle que :
point a ; // attention, point a () serait une déclaration d’une fonction a
point a : est acceptable dans deux situations différentes :
– Il n'existe pas de constructeurs de point,
34
– Il existe un constructeur de point sans argument.
Appel des constructeurs et des destructeurs (suite)
35
Objets dynamiques
structures :
struct S1
{ int x ; Déclaration :
double y ; En C : struct S1 * adr ;
int t [5] ; En C++ : S1 * adr ;
};
•Allocation dynamique
L'instruction : adr = new S1; réalise une allocation dynamique
d'espace mémoire pour un élément de type S1 et affecte son
adresse au pointeur adr.
•Accès aux différents champs:
adr -> y; // ou (*adr).y
•Libération de mémoire :
delete adr ; 36
Objets dynamiques (suite)
class point{
int x, y ; /* déclaration des membres
privés */
public : /* déclaration des membres publics */ •Déclaration:
point (int, int) ; // constructeur1 point * p;
~point () ; // destructeur
void deplace (int, int) ;
void affiche () ;
};
•Allocation dynamique
L'instruction : p= new point(1,4) ;
crée dynamiquement un emplacement de type point (qui contiendra donc
ici la place pour deux entiers) et affecte son adresse à p
•Accès aux fonctions membres de l’objet pointé par p :
p-> affiche () ; //(* p).affiche () ;
p-> x; // si x public
•Libération de mémoire (suppression de l’objet ):
delete p; 37
Utilité des opérateurs new et delete
38
Exemple
#include <iostream> main() {
using namespace std ; void fct (point *) ; // prototype fonction fct
point * p;
class point{
cout << "** Debut main \n" ;
int x, y ; p= new point (3,7) ; //création dynam. d'un objet
public : fct (p) ;
point (int abs, int ord) { // constructeur cout << "** Fin main \n" ;
x=abs ; y=ord ; }
void fct (point * adp) {
cout << "++ Appel Constructeur \n" ;
cout << "** Debut fct \n" ;
} delete adp ; // destruction de cet objet
~point () // destructeur (en fait, inutile ici) cout << "** Fin fct \n" ;
{ cout << "-- Appel Destructeur \n" ; }
}
}; ** Debut main
++ Appel Constructeur
** Debut fct
- - Appel Destructeur
** Fin fct
39
** Fin main
Constructeur de recopie
Mise en évidence du problème
40
Constructeur de recopie (suite)
41
Constructeur de recopie (suite)
42
Constructeur de recopie (suite)
43
Constructeur de recopie (suite)
En général, un constructeur par recopie n’a pas de raison de modifier l’objet passé en
argument. De plus, une fonction prévue pour un objet constant peut toujours s’utiliser
pour un objet non constant (réciproquement fausse)
vector(const vector&);
44
Constructeur de recopie (suite)
45
Quelques règles
• Un constructeur peut comporter un nombre quelconque d’arguments,
éventuellement aucun.
46
Objets automatiques et statiques
• Durée de vie
– Le moment où ils sont créés et celui où ils sont détruits,
– Les règles s'appliquant aux variables ordinaires se transposent tout naturellement aux
objets.
• Les objets automatiques sont créés par une déclaration :
• Dans une fonction : l’objet est créé lors de la rencontre de sa déclaration (celle ci
peut être précédée, au sein de ce bloc, des autres instructions), et détruit à la fin de
l'exécution de la fonction.
• Dans un bloc : l’objet est aussi créé lors de la rencontre de sa déclaration, et
détruit lors de la sortie du bloc.
• Les objets statiques sont ceux créés par une déclaration située :
– En dehors de toute fonction,
– Dans une fonction, mais assortie du qualificatif static.
Les objets statiques sont créés avant le début de l'exécution de la fonction main et détruits
après la fin de son exécution.
• Les objets temporaires
Appel direct au constructeur d’une classe:
point (1,2); //=> objets temporaires
point a(0,0); a = point (1,2); // copier l’objet temporaire dans a
47
Données membres statiques
Les membres statiques sont des variables/ fonctions partagées par toutes
les instances de la classe (données globales partagées ).
• Présents une seule fois en mémoire.
• La visibilité et les droits d'accès sont conservés.
49
Champs de classe (suite)
C++ permet de définir ce qu’on nomme des champs de classe (ou statiques) qui
n’existent qu’en un seul exemplaire, quel que soit le nombre d’objets de la classe.
class B
{ static int n ;
float x ;
}
B a ,b;
On aboutit à cette situation :
50
Exemple 1
main() {
#include <iostream> void fct () ;
using namespace std ; Aa;
class A{ fct () ;
static int cmp; // compteur du nombre d'objets créés Ab;
public : }
A() ; void fct (){
~A() ; A u, v ;
}; }
int A:: cmp = 0 ; // initialisation du membre statique cmp
A::A() // constructeur
{ cout << "++ construction : il y a maintenant " << ++ cmp << " objets\n" ;
}
A::~A() // destructeur
{ cout << "-- destruction : il reste maintenant " << -- cmp << " objets\n" ;
} ++ construction : il y a maintenant 1 objets
++ construction : il y a maintenant 2 objets
++ construction : il y a maintenant 3 objets
-- destruction : il reste maintenant 2 objets
-- destruction : il reste maintenant 1 objets
++ construction : il y a maintenant 2 objets
-- destruction : il reste maintenant 1 objets 51
-- destruction : il reste maintenant 0 objets
Exemple 2
TestStatic.h
class TestStatic { // initialisation de membre statique
static int NbObjets; // Attribut statique int TestStatic::NbObjets=0;
public: int main () {
// constructeur cout << "Nombre d'objets de la classe :"
<< TestStatic ::GetNbObjets() << endl;
TestStatic() {NbObjets++;};
// Affichage du membre statique TestStatic a; a.AfficherNbObjets();
void AfficherNbObjets () { TestStatic b, c;
cout << "Le nombre d'objets crées est : " b.AfficherNbObjets(); c.AfficherNbObjets();
<< NbObjets << endl; TestStatic d;
}; d.AfficherNbObjets(); a.AfficherNbObjets();
}
static int GetNbObjets() {return NbObjets;};
};
Nombre d'objets de la classe : 0
Le nombre d'objets crées est : 1
Le nombre d'objets crées est : 3
Le nombre d'objets crées est : 3
Le nombre d'objets crées est : 4
Le nombre d'objets crées est : 4 52
Exploitation d’une classe
• La classe est considérée comme un composant logiciel
• Jusqu’ici, nous avions regroupé au sein d’un même programme trois sortes
d’instructions destinées à :
– La déclaration de la classe,(point.h)
– La définition de la classe, (point.cpp)
– L’utilisation de la classe. (main.cpp)
• En pratique, on aura souvent intérêt à découpler la classe de son
utilisation.
– C’est tout naturellement ce qui se produira avec une classe d’intérêt
général utilisée comme un composant séparé des différentes
applications.
53
Exercice