Chapitre 1
Introduction à la POO:
– Les classes vs les objets
420-KAB-LG Programmation orientée par objets 1
Programmation procédurale ou orientée par objets?
La programmation procédurale:
La programmation orientée par objets:
Tas de données
Méthode 1 Méthode 2 Méthode 3
Méthode 4 Méthode 5 Méthode 6
Données Données Données
Méthode 1 Méthode 2
Méthode 3 Méthode 4
Méthode 5 Méthode 6
Avantages de la POO
Pour le programmeur:
– Permet de rassembler des données qui vont
logiquement ensemble afin de les traiter comme un tout.
– Protège les données en limitant le traitement à ce qui est prévu par l’objet uniquement.
De façon plus générale:
– Elle augmente l’intelligibilité des programmes en rassemblant les données et les traitements qui
s’appliquent à elles.
– Elle facilite la réutilisation de morceaux de programmes.
– Elle facilite la mise à jour et l’ajout de
fonctionnalités.
420-KAB-LG Programmation orientée par objets 3Un objet...
Définition: « Chose solide ayant unité et indépendance. »
Dans le contexte informatique un objet peut représenter:
– Un objet concret : une chaise, un robot, une personne...
– Un objet abstrait: un compte de banque, un cours de programmation…
Un objet se compare à une variable: il doit être
déclaré avant d’être utilisé et a une durée de vie.
Définition: « Idée abstraite servant à modéliser un ensemble d’objets concrets. »
Une classe se compare à un type. L’existence d’un type ne représente rien tant qu’on n’a pas déclaré une variable.
La classe n’est qu’un moule servant à créer des objets.
Un objet est aussi nommé une instance d’une classe.
Une classe possède des membres:
– Attributs: variables qui représentent les données de l’objet. Les valeurs de ces données sont les états de l’objet. Chaque objet a un ensemble d’attributs (de variables) qui lui est propre.
– Méthodes: actions qui s’appliquent à l’objet ou encore services que peut rendre l’objet.
On déclare des objets d’une classe tout comme on déclare des variables d’un certain type.
La classe est une extension de la notion de structure...
420-KAB-LG Programmation orientée par objets 5
Attributs ou propriétés
Définition: « Une caractéristique ou un
état que possède tous les objets d’une
même classe mais dont le contenu peut
varier d’un objet à l’autre. »
Méthodes
Définition: « Une méthode est un sous- programme qui appartient à une classe et effectue un traitement sur les attributs de cette classe ».
Une méthode peut servir à:
– Modifier la valeur d’un attribut (mutateur) – Obtenir la valeur d’un attribut (accesseur) – Effectuer un traitement plus général
420-KAB-LG Programmation orientée par objets 7
Les modificateurs d’accès
public:
– Le membre est visible à l’intérieur de la classe comme de l’extérieur (interface).
private:
– Le membre n’est visible qu’à l’intérieur de la classe (encapsulation).
La seule différence entre une classe et une
structure est l’accès par défaut (si non spécifié):
– Pour une classe: private
– Pour une structure: public (rétrocompatibilité)
protected:
– Le membre n’est visible qu’à l’intérieur de la classe et
ses dérivées (dans le contexte de l’héritage)
Représentation UML d’une classe
420-KAB-LG Programmation orientée par objets 9
Accès:
- Privé
+ Publique
# Protégé
Méthodes
Attributs
Nom
Une classe a des méthodes et des attributs
(on peut faire la distinction entre un attribut fixe (caractéristique) ou variable (état))
La classe se protège et assure sa cohérence interne!
La classe fournie une interface simple (des méthodes publiques) mais complète!
Un attribut est toujours privé!
Accès à un attribut:
Aucun
Lecture seule (accesseur)
Lecture et écriture (mutateur et accesseur)
Trois grands
principes de la POO
420-KAB-LG Programmation orientée par objets 11
1. L’encapsulation
Définition: « Exposer les fonctionnalités d’un objet tout en cachant les structures de données et les détails d’implémentation. »
Indépendance:
– Afin d’assurer la cohérence interne d’un objet, seul ce dernier doit pouvoir lire et modifier ses attributs. Les attributs devraient donc toujours être privés!
– Les méthodes publiques implémentent l’interface d’utilisation. Elle doit être simple mais complète.
Elle doit aussi assurer la cohérence.
Unité:
– Il existe un lien cohérent entre les attributs.
– Les méthodes doivent lire et/ou modifier les attributs.
2. L’héritage
Permet la généralisation: c’est une caractéristique importante d’un langage naturel!
Simplifie les programmes en évitant la redondance.
420-KAB-LG Programmation orientée par objets 13
3. Le polymorphisme
Définition: « Le polymorphisme consiste à faire abstraction de la forme. »
Prendre
l’automobile Prendre
l’autobus Méthode
abstraite
Un exemple en C++
class CBicyclette { public:
void Freiner();
void Pedaler();
string ObtenirCouleur() const;
void ModifierCouleur(string couleur);
private:
string Couleur_;
float Vitesse_;
int Hauteur_;
};
420-KAB-LG Programmation orientée par objets 15
Écriture d’une classe
Déclaration dans le .h:
class CEntierEncapsule {
int Entier_;
public:
void SetEntier(int NouvelEntier);
int GetEntier() const;
};
Définition dans le .cpp:
void CEntierEncapsule::SetEntier(int NouvelEntier) {
Entier_ = NouvelEntier;
}
int CEntierEncapsule::GetEntier() const {
return Entier_;
}
Utilisation:
int main() {
CEntierEncapsule UnObjet;
UnObjet.SetEntier( 10 );
cout << UnObjet.GetEntier();
}
Procédurale VS Objets
class CPersonne {
public:
int Age;
bool EstAdulte() {
return Age >=18;
} };
int main() {
CPersonne Joan;
Joan.Age = 19;
cout << Joan.EstAdulte();
}
420-KAB-LG Programmation orientée par objets 17
bool EstAdulte(int Param) {
return Param>= 18;
}
int main() {
int Age;
Age= 19;
cout << EstAdulte(Age);
}
Les méthodes constantes
Le mot-clé const après la déclaration
d’une méthode garantit que cette méthode ne modifiera aucun des attributs de la
classe.
C’est une protection pour le programmeur.
C’est une excellente pratique que de déclarer les accesseurs const (ainsi que toute autre méthode qui n’a pas besoin de modifier les attributs de l’objet).
Exemple: int GetEntier() const;
Vers la POO: Avant
float Quotient(int Num, int Den) { float q =0.0f;
if(Den!=0)
q = (float)Num / (float)Den;
return q;
}
int main() { int n=3;
int d=4;
cout << Quotient(3,4) << endl;
}
420-KAB-LG Programmation orientée par objets 19
Ancienne façon de faire un cast
Vers la POO: Naissance
struct Fraction { int Num;
int Den;
}; float Quotient(Fraction Fr) { float q = 0.0f;
if(Fr.Den!=0)
q = static_cast<float>(Fr.Num) / static_cast<float>(Fr.Den) ; return q;
} int main()
{ Fraction Frac;
Frac.Num = 3;
Frac.Den = 4;
cout << Quotient(Frac) << endl;
Apparition d’un nouveau concept et
regroupement des données
Vers la POO: Intégration
class Fraction { public:
int Num;
int Den;
float Quotient()
{
float q = 0.0f;
if(Den!=0)
q = static_cast<float>(Num) / static_cast<float>(Den);
return q;
};
}int main()
{ Fraction Frac;
Frac.Num = 3;
Frac.Den = 4;
cout << Frac.Quotient() << endl;
}
420-KAB-LG Programmation orientée par objets 21Ajout de la méthode à la classe:
Regroupement des données et des
méthodes qui utilisent ces données
Ce qu’il faut retenir...
Il faut bien comprendre les structures (ref.
KA0) pour apprendre la POO.
À partir de maintenant nous ajouterons des fonctions à nos structures!
Nous utiliserons le mot-clé class au lieu de
struct mais il y a peu de différences pour le
compilateur
Chapitre 2
Le cycle de vie
420-KAB-LG Programmation orientée par objets 23
Construction Utilisation Destruction
Les services par défaut d’une classe
1. La construction
– Allocation de la mémoire – Attributs non-initialisés
2. La construction d’une copie
– Allocation de la mémoire
– Copie membre à membre (shallow copy)
3. L’affectation entre objets d’une même classe
– Copie membre à membre (shallow copy)
4. La destruction
– Libération de la mémoire
Les services par défaut peuvent être redéfinis!
Exemple
// Fichier EntierEncapsule.h class CEntierEncapsule
{
public:
int GetEntier() const;
void SetEntier(int Entier);
private:
int Entier_;
};
420-KAB-LG Programmation orientée par objets 25
Exemple (suite)
int main() {
CEntierEncapsule Entier1, // Construction par défaut Entier2;
Entier1.SetEntier(10);
AfficherObjet(Entier1);
Entier2 = Entier1; // Affectation d’un objet à un autre AfficherObjet(Entier2);
} // Destruction des objets
void AfficherObjet(CEntierEncapsule Param)
// Construction d’une copie {
cout << Param.GetEntier() << endl;
} // Destruction de l’objet
// passé en paramètre
Les services par défaut (suite)
Parfois le service par défaut ne suffit pas!
– Par exemple, la construction par défaut n’initialise pas les attributs!
Nous pouvons implémenter nous-même la construction d’un objet, la copie,
l’affectation et la destruction d’un objet.
420-KAB-LG Programmation orientée par objets 27
Les services par défaut (suite)
La déclaration d’un constructeur permet
d’initialiser l’objet après l’allocation de l’objet.
La déclaration d’un constructeur de copie dans une classe remplace le constructeur de copie par défaut.
La déclaration d’un destructeur dans une classe permet de faire le ménage avant la libération de l’objet.
La surcharge de l’opérateur = remplace le
service par défaut d’affectation.
Le constructeur
Qu’affiche à l’écran le programme suivant?
int main() {
CEntierEncapsule UnObjet;
cout << UnObjet.GetEntier() << endl;
}
Une valeur aléatoire car la construction par défaut n’initialise pas les attributs! (comme au laboratoire!)
420-KAB-LG Programmation orientée par objets 29
Le constructeur (suite)
Il faudrait plutôt faire:
int main() {
CEntierEncapsule UnObjet;
UnObjet.SetEntier(20);
cout << UnObjet.GetEntier() << endl;
}
Cela ne devrait pas être la responsabilité de
l’utilisateur de la classe d’initialiser l’objet, mais
à l’objet lui-même de s’initialiser correctement!
Le constructeur (suite)
Lors de la construction d’un objet, le compilateur
invoque automatiquement une méthode qui se charge de l’initialisation de l’objet. Cette méthode spéciale se
nomme un constructeur.
Il doit initialiser tous les attributs et exécuter toutes autres instructions requises pour initialiser l’objet.
N’a pas de valeur de retour (même pas void!)
Porte le même nom que la classe.
Peut avoir des paramètres ou non.
On peut définir plusieurs constructeurs pour une même classe, mais ils doivent avoir des signatures différentes.
420-KAB-LG Programmation orientée par objets 31
Le constructeur sans paramètres
Il est unique!
class CEntierEncapsule {
public:
CEntierEncapsule(); // Même nom que la classe // pas de type de retour!
};
CEntierEncapsule::CEntierEncapsule() // Définition {
Entier_ = 0; // ou encore mieux SetEntier(0);
}
Le constructeur sans paramètre (suite)
Et maintenant, qu’affiche à l’écran le programme suivant?
int main() {
CEntierEncapsule UnObjet;
cout << UnObjet.GetEntier() << endl;
}
Il affiche 0 ! N’est-ce pas génial??
420-KAB-LG Programmation orientée par objets 33
Les constructeurs avec paramètre(s)
class CEntierEncapsule {
public:
CEntierEncapsule(int Valeur); // Même nom que la classe // pas de type de retour!
}; // un paramètre int
CEntierEncapsule::CEntierEncapsule(int Valeur) {
Entier_ = Valeur; //ou encore mieux SetEntier(Valeur);
}
paramètres (suite)
int main() {
CEntierEncapsule UnObjet(99);
cout << UnObjet.GetEntier() <<
endl;
}
Il affiche 99 !
420-KAB-LG Programmation orientée par objets 35
La valeur par défaut d’un paramètre
En C++, un paramètre peut avoir une valeur par défaut.
La valeur est définie dans le prototype seulement (.h).
Si le paramètre n’est pas spécifié lors de l’appel, la valeur par défaut est utilisée.
Exemple:
void Afficher(string Message="Bonjour", int Fois = 1);
Peut être appelé de trois façons:
Afficher("Salut", 5); // Affiche Salut 5 fois Afficher("Allo"); // Affiche Allo 1 fois
Afficher(); // Affiche Bonjour 1 fois
Par contre, Afficher(5) donne une erreur de compilation…
constructeurs
On peut donc remplacer plusieurs constructeurs par un seul constructeur avec des paramètres avec des valeurs par défaut!
Évite la redondance du code.
class CPersonne {
public:
CPersonne(string Nom="Roger", int Age=20);
// remplace avantageusement:
// CPersonne();
// CPersonne(string Nom);
// CPersonne(string Nom, int Age);
};
420-KAB-LG Programmation orientée par objets 37
Le constructeur de copie
Appelé de façon implicite lorsque:
1. Un objet est passé en tant que paramètre par valeur
2. Un objet est retourné par une fonction
3. Un objet est construit à partir d’un autre objet
Son rôle est de créer une copie exacte de l’objet passé en paramètre
Sa signature est toujours la même:
– NomClasse (const NomClasse& Original);
Le constructeur de copie
class CEntierEncapsule {
public:
CEntierEncapsule(const
CEntierEncapsule& Original);
};
CEntierEncapsule::CEntierEncapsule(const CEntierEncapsule& Original)
{
Entier_ = Original.GetEntier();
// ou encore mieux: SetEntier(Original.GetEntier() );
}
Pourquoi un passage par référence??
420-KAB-LG Programmation orientée par objets 39
passage par valeur…
void Test(A p) {
}
class A {
public:
A(A p);
};
int main() {
A a;
Test(a);
}
Appel de p.CtrCopie(a)
Appel de p.CtrCopie(a)
Appel de p.CtrCopie(a)
Appel de p.CtrCopie(a)
Appel de p.CtrCopie(a)
Un paramètre déclaré const indique qu’il ne pourra pas être modifié à l’intérieur de la méthode.
On peut aussi déclarer un objet local constant.
Sur les objets et les paramètres const, on ne peut appeler que des méthodes const, pour respecter la garantie!
Exemple:
CEntierEncapsule::CEntierEncapsule(const CEntierEncapsule& Original)
420-KAB-LG Programmation orientée par objets 41
Le const est un virus…
class CRect {
int x_,y_;
public:
CRect(const CRect& Autre);
void GetX() ; void GetY() ; };
CRect::CRect(const CRect& Autre) {
x_ = Autre.GetX();
}
Erreur: impossible de convertir un pointeur 'this' de 'const
CRect&' en ‘CRect&'
Le destructeur
N’a pas de valeur de retour
A le même nom que la classe préfixé du symbole ~
Effectue les instructions appropriées lorsque l’objet est détruit
– Fermeture d’un fichier
– Destruction de mémoire allouée dynamiquement
Pour l’instant, le service par défaut fera l’affaire!
420-KAB-LG Programmation orientée par objets 43
Le destructeur (suite)
class CEntierEncapsule {
public:
~CEntierEncapsule();
};
CEntierEncapsule::~CEntierEncapsule() {
cout << "Rien à faire pour l’instant…" << endl;
}
int main() {
CEntierEncapsule UnObjet(99);
cout << UnObjet.GetEntier() << endl;
} Appel automatique du destructeur!
Dès qu’au moins un constructeur est défini, le service de construction par défaut disparait!
Un constructeur sans paramètre est souvent essentiel (tableaux, composition, héritage, etc.)
Attention de ne pas créer des situations ambiguës:
class CPasClair {
CPasClair();
CPasClair(int i=3);
};
int main() {
CPasClair a; // error C2668: 'CPasClair::CPasClair' : // appel ambigu à une fonction surchargée }
420-KAB-LG Programmation orientée par objets 45
Chapitre 3
Les relations
Diagramme de classes UML
420-KAB-LG Programmation orientée par objets 47
Relations entre les classes
Il existe plusieurs types de relations entre deux classes:
– Aucune relation
Classes complètement indépendantes
– Utilisation
Simple dépendance: paramètre,
variable locale ou valeur de retour
(suite)
– Composition
– Agrégation
– Héritage
420-KAB-LG Programmation orientée par objets 49
La relation crée une dépendance
La classe A qui a une relation avec la classe B devient dépendante de cette dernière
Une classe dépendante d’une autre classe fait des #include « B.h »
La modification d’une classe
– provoque normalement la recompilation des classes dépendantes (erreurs?)
– Peut amener un mauvais fonctionnement des
classes dépendantes
Première relation:
L’utilisation
420-KAB-LG Programmation orientée par objets 51
L’utilisation
Une classe peut utiliser une autre classe de trois façons:
– En paramètre d’une méthode
– En variable locale dans une méthode
– En valeur de retour d’une méthode
…en paramètre
type A::Methode(B b) type A::Methode(B& b)
type A::Methode(const B& b) Exemples:
bool CBanque::Bloquer(CCompte c);
void CConsole::
Afficher(const string& Message);
void CFenetre::Afficher
(CImage& Image, int x, int y);
420-KAB-LG Programmation orientée par objets 53
Copie!
… en variable locale
type A::Methode() {
B b;
}
bool CResto::Payer(float Montant) {
CFacture Facture(Montant);
Facture.Imprimer();
//…
}
… en valeur de retour
B A::Methode() {
B b;
// …
return b;
}
Exemple:
COeuf CPoule::Pondre() {
COeuf UnOeuf;
// …
return UnOeuf;
}
420-KAB-LG Programmation orientée par objets 55Copie!
Copie!
Deuxième relation:
La composition
class CAuto {
public:
CAuto();
~CAuto();
int GetNbChevaux() const;
void SetNbChevaux(int NbChevaux);
void DegonflerUneRoue(int i);
private:
int NbChevauxDuMoteur_;
int PressionDesRoues_[4];
};
420-KAB-LG Programmation orientée par objets 57Classes CRoue et CMoteur
CRoue
-Pression_:int +Dégonfler()
CMoteur -NbChevaux_:int
+GetNbChevaux():int
+SetNbChevaux(entrée NbChevaux:int)
Classe CAuto améliorée
420-KAB-LG Programmation orientée par objets 59
class CAuto {
public:
CAuto();
~CAuto();
CRoue& GetRoue(int Index);
void Modifier(int NbChevaux);
private:
CMoteur& GetMoteur();
CMoteur SuperMoteur_;
CRoue Roues_[4];
};
La composition
La composition est un type de relation entre deux classes qui implique une notion de
contenance.
On dit que A et B sont deux classes reliées par une relation de composition, c’est-à-dire que A est composée de B
A = Composée ; B= Composant
La composition
Exemples:
Un objet Auto se compose d’objets moteur et roues.
Un objet Hôtel se compose d’objets ChambreHôtel
Un objet Ordinateur se compose d’objets CPU, clavier et écran
Un objet mammifère se compose d’objets tête, 4 pattes
420-KAB-LG Programmation orientée par objets 61
La composition
Le composant est utilisé comme attribut du composé.
Les cycles de vie du composé et ses composants sont intimement liés.
Il s’agit d’une agrégation forte: si le
composé est détruit, ses composants
disparaissent aussi
Construction / destruction d’une composition
Si un objet B est un attribut de l’objet A, le constructeur de l’objet B sera appelé avant celui de l’objet A.
Ceci est logique: pour construire une auto, il faut d’abord construire ses composantes,
comme le moteur et les roues.
420-KAB-LG Programmation orientée par objets 63
Raison: Accès possible
Construction / destruction d’une composition
Lors de la destruction c’est l’inverse
Si on veut utiliser un autre constructeur que le constructeur par défaut (ou mixte), on peut
sélectionner le constructeur à utiliser lors de la
pré-construction
La pré-construction
CAuto::CAuto() : SuperMoteur_(90) {
cout << "Constructeur de CAuto" <<
endl;
}
On peut même utiliser cette syntaxe pour initialiser tous les attributs :
CChat::CChat(int Age, string Nom) : Age_(Age), Nom_(Nom) { }
420-KAB-LG Programmation orientée par objets 65
Agrégation forte vs faible
Agrégation forte = Composition
Agrégation faible : Le composant ne disparaît pas si le composé est détruit.
Exemples: (Aéroport et avions), (Train et
wagons), (groupe et étudiants), (Ferme et
animaux)
Représentation UML de la composition
420-KAB-LG Programmation orientée par objets 67
L’encapsulation appliquée à la composition
class CAuto {
public:
CAuto();
~CAuto();
const CRoue& GetRoue(int Index);
void Modifier(int NbChevaux);
private:
CMoteur& GetMoteur();
CMoteur SuperMoteur_;
CRoue Roues_[NBROUES];
};
Pas d’accès direct au moteur
Exécution du programme Auto
420-KAB-LG Programmation orientée par objets 69
Troisième relation:
L’héritage
( 2 e grand principe de la POO)
Motivation
420-KAB-LG Programmation orientée par objets 71
Généralisation
Classe de base, parent
ou générale Classes
dérivées, enfants ou spécialisées
Dérive de...
Hérite de...
Est une...
Is a…
L’Héritage
L’héritage offre la possibilité de créer une classe à partir d’une autre.
La nouvelle classe bénéficie des attributs et des méthodes de la classe dont elle
dérive. On dira alors que la classe enfant hérite du parent ou que la classe dérivée hérite de la classe de base. Dans la classe dérivée, on peut définir de nouveaux
membres afin de modifier et de spécialiser la classe de base.
420-KAB-LG Programmation orientée par objets 73
Déclaration de CPersonne
class CPersonne {
int Age_;
public:
CPersonne(int Age);
~CPersonne();
int GetAge() const;
void SeDéplacer();
};
Classe normale,
rien ne change!
class CEtudiant : public CPersonne {
int Note_;
public:
CEtudiant(int Age, int Note);
~CEtudiant();
int GetNote() const;
void Etudier();
};
420-KAB-LG Programmation orientée par objets 75
Indique que la
classe dérive
de CPersonne
Déclaration de Enseignant
class CEnseignant : public CPersonne
{
int Salaire_;
public:
CEnseignant(int Age, int Salaire);
~CEnseignant();
int GetSalaire() const;
void Enseigner();
};
Indique que la
classe dérive
de CPersonne
int main() {
CEtudiant Paul(19, 85);
Paul.Etudier();
cout << Paul.GetNote();
Paul.SeDéplacer();
cout << Paul.GetAge();
CEnseignant Joan(35, 15000);
Joan.Enseigner();
cout << Joan.GetSalaire();
Joan.SeDéplacer();
cout << Joan.GetAge();
}
420-KAB-LG Programmation orientée par objets 77Méthodes héritées de
la classe parent
Méthodes héritées de
la classe
parent
Les modificateurs d’accès
Les membres publics d’une classe sont utilisables par toutes les fonctions.
Les membres privés d’une classe sont utilisables uniquement par les fonctions membres de cette classe.
Les membres protégés d’une classe sont
utilisables par les fonctions membres de
cette classe ou d’une classe dérivée.
420-KAB-LG Programmation orientée par objets 79
Construction et destruction
Lors de la création d’un objet de la classe CEtudiant, il y a deux constructeurs
impliqués. Lorsqu’un objet CEtudiant est détruit, il y a deux destructeurs qui
participent à la destruction de l’objet.
1. Quel est l’ordre d’appel des constructeurs et des destructeurs?
2. Comment faire pour déterminer cet ordre?
3. Pourquoi cet ordre précis?
La pré-construction
Lors de la pré-construction, on peut sélectionner le constructeur de la classe de base à utiliser pour acheminer de l’information à la classe de base:
CEtudiant::CEtudiant(int Age, int Note): CPersonne(Age)
{
SetNote(Note);
}
La pré-construction peut aussi servir à
sélectionner les constructeurs à utiliser lors de la construction d’un composé.
420-KAB-LG Programmation orientée par objets 81
Chapitre 4
Pointeurs, allocation dynamique et
références
420-KAB-LG Programmation orientée par objets 8383
Les pointeurs
(indirection, raccourci, référence) 0x20
0x24
5
0x20
Les adresses
On accède à l’adresse d’une variable (sa position en mémoire) avec le symbole &
On accède au contenu d’une adresse avec le symbole *
int a = 3;
cout << a; // Affiche 3
cout << &a; // Affiche 0068FDF4
cout << *(&a); // Affiche 3, *(&a) = a
La variable a peut être de type primitif (int, char, …) ou un objet
* &
Les pointeurs
Un pointeur contient l’adresse d’une variable ou d’un objet.
Déclaration d’un pointeur:
int* p; // Pointeur vers un int
CEntier* pEntier; // Pointeur vers un CEntier short a, *b; // a est un short, b est
// pointeur vers un short
420-KAB-LG Programmation orientée par objets 85
Exemple d’utilisation d’un pointeur (1)
short a, *b;
*a = 5; // invalide: a n’est pas un // pointeur
b = &a; // valide: b est un pointeur de
// short, et &a est l'adresse d'un // short
*b = 5; // valide: b est un pointeur de
// short. Cette opération a pour
// effet de déposer la valeur 5
// là où pointe b... donc dans a
Exemple d’utilisation d’un pointeur (2)
int a, // valeur de a: indéterminée b= 3; // valeur de b: 3
int *p; // p est un pointeur vers un int p= &a; // p reçoit l’adresse de a
// donc p pointe vers a
*p= 4; // le contenu pointé par p // ( donc a ) reçoit 4
b+= a; // b devient égal à 7!
420-KAB-LG Programmation orientée par objets 87
Visualiser les pointeurs
int a, b= 3;
int *p;
p= &a;
*p= 4;
Initialiser un pointeur
int *p = nullptr;
// (…)
if (p != nullptr) {
// ... p mène à un endroit valide }
else {
// ... utiliser *p serait imprudent!
...
}
420-KAB-LG Programmation orientée par objets 89
En C, on utilisait la constante NULL En C++ on utilisait l’adresse 0 (zéro)
En C++11, on utilise nullptr
Déclaration vs instruction
Déclaration Instruction
* Déclare un pointeur int *p;
void fonction(int *p);
Ce qui est pointé par cout << *p;
a = *p;
Fonction(*p);
& Déclare une référence int &r = i;
void fonction(int& r);
L’adresse de cout << &i;
Fonction(&i);
Pointeurs et POO
CEntier UnEntier(4);
// Affiche 4
cout << UnEntier.GetEntier();
// Affiche 0072AB00 cout << &UnEntier;
// Affiche 4
cout << (*(&UnEntier)).GetEntier();
420-KAB-LG Programmation orientée par objets 91
Pointeurs et POO (suite)
// Déclaration d’un pointeur vers un // objet
CEntier *pEntier = nullptr;
// Allocation dynamique d’un objet pEntier = new CEntier(3);
// Appel des méthodes de l’objet pointé // par pEntier
(*pEntier).SetEntier(3);
cout << (*pEntier).GetEntier() << endl;
L’opérateur ->
L’opérateur -> est une syntaxe abrégée qui permet de sélectionner un membre d’un
objet pointé par un pointeur:
(*pEntier).SetEntier(3);
est identique à
pEntier->SetEntier(3);
420-KAB-LG Programmation orientée par objets 93
Passer un pointeur en paramètre
void Afficher(CEntier* pEntier) {
cout << pEntier->GetEntier();
}
int main() {
CEntier Entier(3);
CEntier *pEntier = new CEntier(4);
Afficher(&Entier);
Afficher(pEntier);
}
Passer un pointeur en paramètre (suite)
Très similaire au passage par référence
Il n’y a pas de copie d’objets effectués
Les modifications apportées à l’objet dans la méthode ou la fonction se répercuteront à l’extérieur de la méthode ou de la
fonction.
Cependant, contrairement à la
référence, un pointeur peut être nul ou invalide.
420-KAB-LG Programmation orientée par objets 95
Retourner un pointeur
CEntier* Creer(int Valeur) {
return new CEntier(Valeur);
}
int main() {
CEntier *pEntier = Creer(5);
cout << pEntier->GetEntier();
}
Erreur à ne jamais faire! (1)
CEntier* Creer(int Valeur) {
CEntier EntierLocal(Valeur);
return &EntierLocal;
}
int main() {
CEntier *pEntier = Creer(5);
cout << pEntier->GetEntier();
}
420-KAB-LG Programmation orientée par objets 97
Détruire un objet créé dynamiquement
int main() {
CEntier *pEntier = new CEntier(6);
cout << pEntier->GetEntier();
// …
delete pEntier;
}
Il est de bon usage que celui qui crée un
objet soit responsable de le détruire.
Erreur à ne jamais faire! (2)
CEntier Entier(3);
CEntier *pEntier = nullptr;
CEntier *pEntier2 = nullptr;
pEntier = &Entier;
pEntier2 = pEntier;
delete pEntier;
delete pEntier2;
420-KAB-LG Programmation orientée par objets 99
Pile vs Tas
int main() {
int i = 2;
CEtudiant *p = nullptr;
p = new CEtudiant();
test(i);
delete p;
}
void test(int j) {
cout << j;
}
Pile
2 (i) 2 (i) null (p) null (p)
CEtudiant CEtudiant
2 (j)
2 (j)
0xBB 0xBB (p) 0xBB (p)
420-KAB-LG Programmation orientée par objets 101 420-KAB-LG Programmation orientée par objets 101
L’allocation dynamique
(et application du cycle de vie)
Le tableau automatique (rappel)
La taille du tableau doit être connue à la compilation:
int TabEntiers [ 100 ];
Encore mieux:
const int NbCases = 100;
int TabEntiers [ NbCases ];
Exemples qui ne compilent pas
Exemple 1:
int NbCases = 15;
int TabValeurs [ NbCases ];
Exemple 2:
int NbCases;
cin >> NbCases;
int TabValeurs [ NbCases ];
Exemple 3:
void CreerTableau(int Taille) {
const int TAILLE = Taille;
int Tableau[TAILLE];
}
420-KAB-LG Programmation orientée par objets 103
Le tableau dynamique
Un tableau dynamique peut avoir une taille différente à chaque exécution
L’espace mémoire est réservé à l’exécution et non à la compilation comme un tableau automatique
Le tableau est alloué sur le tas et non sur la pile
L’utilisation d’un tableau dynamique se fait en 5 étapes:
1. Déclaration d’un pointeur pour contenir l’adresse mémoire (le point de départ) du tableau
2. Réservation de l’espace mémoire avec l’opérateur new []
3. Conserver la taille du tableau dans une variable
4. Utiliser le tableau comme s’il était un tableau automatique 5. Lorsque le tableau n’est plus requis, libérer l'espace en
mémoire avec l’opérateur delete [ ]
int NbCases; // Taille du tableau
int *TabEntiers; // Pointeur pour retenir l’adresse // du tableau dynamique.
cin >> NbCases; // Demander la taille à l’usager
// Allocation de la mémoire et affectation de l’adresse TabEntiers = new int [NbCases];
// Utilisation du tableau
for (int i = 0; i < NbCases; i++) {
TabEntiers[i] = i * i;
cout << TabEntiers[i] << endl;
}
// Destruction de la mémoire allouée.
delete [] TabEntiers;
420-KAB-LG Programmation orientée par objets 105
Automatique vs dynamique
int main() {
const int Nb=10;
int Tab[Nb];
// utilisation …
}
int main() {
int Nb=10;
int* Tab = new int[Nb];
// utilisation … delete [] Tab;
}
Les tableaux automatiques
– De types primitifs: int Notes[10];
– De type utilisateurs
De structures: Compte MesComptes[10];
De classes: CTasse MesTasses[10];
– De pointeurs: CTasse* MesTassesPtr[10];
C’est toujours la même syntaxe:
Type Nom[Taille];
420-KAB-LG Programmation orientée par objets 107
Les tableaux - Résumé
Les tableaux dynamiques
– De types primitifs
int* Tab = new int [10]; … delete [] Tab;
– De type utilisateurs
De structures
Point* Flocs = new Point [10]; … delete [] Flocs;
De classes
CTasse* Tasses = new CTasse[10]; … delete [] Tasses;
– De pointeurs!
CTasse** Tasses = new CTasse*[10]; … delete [] Tasses;
C’est toujours la même syntaxe:
Type* Nom = new Type[Taille]; … delete [] Nom;
!
Les tableaux d’objets
Lorsqu’on déclare un tableau d’objets:
CMenu* MesMenus = new CMenu[10];
Ou
CMenu MesMenus[10];
Il y a appel du constructeur par défaut pour chaque élément du tableau
Si aucun constructeur n’est défini: utilisation du constructeur automatique
Si un constructeur est défini, mais pas le
constructeur par défaut: erreur de compilation!
(le constructeur de copie échappe à cette règle)
420-KAB-LG Programmation orientée par objets 109
La fuite mémoire
Défaut de programmation grave et difficile à trouver!
Outils de vérification de code (BoundsChecker)
Exemple:
void Test() {
int* Tab = new int[10];
// … }
for(int i=0;i<1000000000;i++) char *c = new char[10];
Le delete [] a été oublié!
Ne pas essayer à la maison …
Retour sur les tableaux d'objets
Exemple 1 - Un objet
int main() {
CSou Sou;
Sou.Deplacer();
}
420-KAB-LG Programmation orientée par objets 111
Appel d'un constructeur
Appel du destructeur
Objet sur la
Pile
Exemple 2 - Un tableau automatique d’objets
int main() {
const int Nb = 5;
CSou Sou[Nb];
for(int i=0;i<NB;i++) {
Sou[i].Deplacer();
} }
Appels des constructeurs par défaut
Appels des destructeurs
Tableau sur la
Pile
Exemple 3 - Un tableau dynamique d’objets
int main() {
int Nb = 5;
CSou* pSou = new CSou[Nb];
for(int i=0;i<Nb;i++) {
pSou[i].Deplacer();
}
delete[] pSou;
}
420-KAB-LG Programmation orientée par objets 113Appels des constructeurs par défaut
Appels des destructeurs Tableau
dans le
tas
Exemple 4 - Un pointeur d’objet
int main() {
CSou* pSou = new CSou;
pSou->Deplacer();
delete pSou;
}
Un objet ou un tableau dans le tas n'est pas détruit automatiquement lorsque la fonction se termine, il faut s'assurer de toujours garder un pointeur...
Appel d'un constructeur
Objet dans le
tas
Appel du destructeur
automatique de pointeurs d’objets (ex 4.8)
int main() {
const int Nb = 5;
CSou* pSou[Nb];
for(int i=0;i<Nb;i++) pSou[i] = new CSou;
for(int i=0;i<Nb;i++) pSou[i]->Deplacer();
for(int i=0;i<Nb;i++) delete pSou[i];
}
420-KAB-LG Programmation orientée par objets 115Appel d'un constructeur Objets dans le
tas
Appel du destructeur Tableau de pointeurs
sur la pile
Exemple 6 - Un tableau dynamique de pointeurs d’objets (optionnel)
int main() {
int Nb = 5;
CSou** pSou;
pSou = new CSou*[Nb];
for(int i=0;i<Nb;i++) pSou[i] = new CSou;
for(int i=0;i<Nb;i++) pSou[i]->Deplacer();
for(int i=0;i<Nb;i++) delete pSou[i];
delete[] pSou;
}
Appel d'un constructeur
Appel du destructeur Tableau de pointeurs
dans le tas
Objets dans le tas
CTabEntiers::CTabEntiers(int Taille) { Table_ = new int[Taille];
Taille_ = Taille;
}
CTabEntiers::~CTabEntiers() { delete [] Table_;
}
void CTabEntiers::SetElement(int Index, int Entier) {
Table_[Index] = Entier;
}
int CTabEntiers::GetElement(int Index) const {
return Table_[Index];
}
420-KAB-LG Programmation orientée par objets 117
(suite)
Que se passes-t-il lorsqu’on passe un objet de la classe CTabEntiers en paramètre par valeur?
int main() {
CTabEntiers Tab(3);
Test(Tab);
}
Que se passe-t-il lorsqu’on construit un objet de la classe CTabEntiers à partir d’un autre?
int main() {
CTabEntiers Tab1(20);
CTabEntiers Tab2(Tab1);
void Test(CTabEntiers Param) {
// …
} Boum!
Re-
Boum!
Encapsulation d’un tableau d’entiers (suite)
Il faut donc absolument définir le constructeur de copie et l’opérateur d’affectation de la classe CTabEntiers:
public:
CTabEntiers(const CTabEntiers& Original);
CTabEntiers& operator=(const CTabEntiers&
Original);
420-KAB-LG Programmation orientée par objets 119
La trinité
Constructeur de copie Opérateur d’affectation
Destructeur
Encapsulation d’un tableau d’entiers
Afin de compléter les fonctionnalités de la classe CTabEntiers, pouvez-vous écrire une méthode qui permettra de modifier la taille du tableau?
public:
void ModifierTaille(int NouvelleTaille,
bool ConserverValeurs);
420-KAB-LG Programmation orientée par objets 121121
Les références
&
Les références
Tout comme le pointeur, la référence représente l’adresse d’un objet
Contrairement au pointeur, la référence ne
référera qu’à une seule et même chose tout au long de son existence, et devra conséquemment référer à quelque chose dès sa déclaration.
On déclare une référence en préfixant son nom d’un “&”. Une référence peut être considérée comme étant au même endroit, en mémoire, que ce à quoi elle réfère. C’est un synonyme, un
alias.
Les références (exemple)
int main () {
int i = 5; // i est un int de valeur 5 int *p = 0; // p pointe « nulle part » ! int &r = i; // r est une réference à i
i++; // i devient 6, r aussi (car r // réfère à i)
r++; // i devient 7, r aussi p = &i; // p pointe vers i
(*p)++; // i, r et *p deviennent 8 p = &r; // p pointe là où r réfère (p
// pointe vers i) }
420-KAB-LG Programmation orientée par objets 123
Paramètre et retour par référence d’un objet
CEntier ParValeur(CEntier Param) {
Param.SetEntier(3);
return Param;
}
CEntier& ParReference(CEntier& Param) {
Param.SetEntier(3);
return Param;
}
Paramètre et retour par
référence d’un objet (suite)
CEntier Entier(1);
for(int i=0;i<1000;++i) {
// 2000 copies d’objets effectuées // 1000 affectations
Entier = ParValeur(Entier);
}
for(int i=0;i<1000;++i) {
// 0 copie d’objet effectuée
// 1000 affectations (qui pourraient être évitées) Entier = ParReference(Entier);
}
420-KAB-LG Programmation orientée par objets 125
void ParValeur(CEntier Param) {
Param.SetEntier(3);
}
void ParReference(CEntier& Param) {
Param.SetEntier(3);
}
int main () {
CEntier Entier(1);
ParValeur(Entier);
cout << Entier.GetEntier(); // Ceci affiche 1 ParReference (Entier);
cout << Entier.GetEntier(); // Ceci affiche 3
}
Les références constantes
int i = 5; // i est un int de valeur 5 const int *p = 0; // p pointe « nulle part »!
const int &r = i; // r est une référence à i i++; // i devient 6, r aussi
// (car r réfère à i)
r++; // INTERDIT!!!
p = &i; // p pointe vers i (*p)++; // INTERDIT!!!
p = &r; // p pointe là où r réfère // (p pointe vers i)
420-KAB-LG Programmation orientée par objets 127
Les références constantes et les objets
Il est souvent préférable d’utiliser une référence constante plutôt que de faire un passage par copie.
Une référence constante sur un objet interdit d’appeler une de ses méthodes qui n’est pas constante car seules les méthodes constantes garantissent que l’objet ne sera pas modifié.
void ReferenceConstante(const CEntier& Param) {
// INTERDIT si CEntier::SetEntier(int) // n’est pas const!
Param.SetEntier(3);
}
class CTableau {
CEntier Tableau_[10];
public :
// Le const permet de ne pas briser l’encapsulation!
const CEntier& GetElement(int Index);
void SetElement(int Index, int Entier);
};
int main () {
CTableau Tab;
// INTERDIT car CEntier::SetEntier(int) // n’est pas const
Tab.GetElement(0).SetEntier(3);
Tab.SetElement(0,3); // Ok!
}
420-KAB-LG Programmation orientée par objets 129Chapitre 5 - Le polymorphisme
3 e grand principe de la POO
« Qui peut prendre plusieurs
formes »
La redéfinition de méthodes
Redéfinition (override) != Surcharge (overload)
420-KAB-LG Programmation orientée par objets 131
La redéfinition de méthodes (suite)
Méthodes avec la même signature, mais une définition différente:
void CPersonne::SeDéplacer() { cout << "Marcher";}
void CEtudiant::SeDéplacer()
{ cout << "Prendre son vélo";}
void CEnseignant::SeDéplacer()
{ cout << "Prendre sa BMW" ; }
La redéfinition de méthodes (suite)
Les classes dérivées ont donc deux versions de la méthode SeDéplacer. Déterminez la version de la méthode utilisée dans les situations suivantes:
CPersonne John;
CEtudiant Bobby;
CEnseignant Joan;
John.SeDéplacer();
Bobby.SeDéplacer();
Joan.SeDéplacer();
420-KAB-LG Programmation orientée par objets 133
Les pointeurs d’objets
Si on déclare un pointeur vers un int, ce pointeur doit pointer vers un int. Si on fait pointer le pointeur de int vers un char, on obtient une erreur de compilation :
int *p = new char;
main.cpp(86) : error C2440:
'initialisation' : impossible de convertir de 'char *' en 'int *'
Pour atteindre le polymorphisme, cette règle doit être assouplie pour les objets:
Si on déclare un pointeur vers un objet de la classe
A, ce pointeur doit pointer vers un objet de la
classe A ou un objet d’une classe dérivée de A.
Pouvez-vous déterminer les lignes qui provoquent une erreur de compilation?
CPersonne* Obj1 = new CPersonne;
CPersonne* Obj2 = new CEtudiant;
CPersonne* Obj3 = new CEnseignant;
CEtudiant* Obj4 = new CPersonne;
CEtudiant* Obj5 = new CEtudiant;
CEtudiant* Obj6 = new CEnseignant;
CEnseignant* Obj7 = new CPersonne;
CEnseignant* Obj8 = new CEtudiant;
CEnseignant* Obj9 = new CEnseignant;
Il y a donc maintenant une différence entre le type de pointeur utilisé et le type réel de l’objet!!
420-KAB-LG Programmation orientée par objets 135
d’objets
Déterminez la version de la méthode utilisée dans les exemples suivants:
CPersonne* Obj1 = new CPersonne;
CPersonne* Obj2 = new CEtudiant;
CPersonne* Obj3 = new CEnseignant;
CEtudiant* Obj5 = new CEtudiant;
CEnseignant* Obj9 = new CEnseignant;
Obj1->SeDéplacer();
Obj2->SeDéplacer();
Obj3->SeDéplacer();
Obj5->SeDéplacer();
Obj9->SeDéplacer();
Par défaut, la version de la méthode utilisée est
déterminée par le type de pointeur utilisé.
(ou virtuelles)
Idéalement, la version de la méthode utilisée
devrait être déterminée par le type réel de l’objet.
Pour ce faire, il faut ajouter le mot-clé virtual aux déclarations des méthodes:
class CPersonne {
// …
virtual void SeDéplacer();
// … };
420-KAB-LG Programmation orientée par objets 137
Méthodes polymorphes (suite)
En supposant que la méthode SeDéplacer est virtuelle, déterminez la version de la méthode utilisée dans les exemples suivants:
CPersonne *Obj1 = new CPersonne;
CPersonne *Obj2 = new CEtudiant;
CPersonne *Obj3 = new CEnseignant;
CEtudiant *Obj5 = new CEtudiant;
CEnseignant *Obj9 = new CEnseignant;
Obj1->SeDéplacer();
Obj2->SeDéplacer();
Obj3->SeDéplacer();
Obj5->SeDéplacer();
Obj9->SeDéplacer();
int main() {
const int N=100;
CPersonne* Tab[N]; // Tableau de pointeurs for(int i=0;i<N;++i)
{
// Le type d’objet alloué est aléatoire!
if(rand()%2==0) Tab[i] = new CEtudiant;
else Tab[i] = new CEnseignant;
}
// La méthode SeDeplacer change selon
// le type réel de l’objet (plusieurs formes)
for(int i=0;i<N;++i)
Tab[i]->SeDéplacer();
for(int i=0;i<N;++i) delete Tab[i];
}
420-KAB-LG Programmation orientée par objets 139Ces deux simples petits lignes illustrent
toute la beauté du
polymorphisme!
Appeler la méthode de la classe de base
Pour effectuer une partie du traitement, la méthode d’une classe dérivée peut appeler la méthode de la classe de base:
void CEtudiant::SeDéplacer() {
// …
// Pour éviter la confusion, il faut // spécifier le nom de la classe de base CPersonne::SeDéplacer();
//…
}
Que se passe-t-il si on oublie le CPersonne:: ?
La méthode virtuelle pure et classe abstraite
La méthode virtuelle pure n’a pas de définition dans la classe de base (=0)
class CPersonne {
virtual void SeDéplacer()=0;
};
Il est donc obligatoire de redéfinir les méthodes virtuelles pures dans les classes dérivées
On ne peut pas créer d’objets d’une classe qui possède une ou des méthodes virtuelles pures
On dira donc de cette classe qu’elle est abstraite
420-KAB-LG Programmation orientée par objets 141
Chapitre 6 – Bibliothèque standard de C++
1. Les entrées / sorties (istream, ostream)
2. Les fichiers (ifstream, ofstream)
3. Les chaînes de caractères (string)
Ensemble de classes et de fonctions standards
Contient aussi la bibliothèque standard du C
Contient la STL (Standard Template Library)
Fait partie de la norme ISO du C++11
Elle contient différents modules:
– Entrées/sorties
– Chaînes de caractères – Structures de données – Algorithmes
– Temps, tâches, etc.
using namespace std;
420-KAB-LG Programmation orientée par objets 143