POOC++2023
POOC++2023
POOC++2023
Orientée Objet
(en C++)
L2 Informatique
Université Cheikh Anta Diop
M. DEYE
Plan du cours
Introduction
Qu'est ce que la POO ?
Minimum sur les entrées sorties
Rappel sur les références et les pointeurs
Spécificités de C++ par rapport à C
Classes et objets en C++
Construction et initialisation d’objets
Héritage
Sur-définition d'opérateurs =, *, <<, >>, ()
2
Introduction
L’histoire de la programmation a commencé
avec l’invention de l’ordinateur
4
Introduction
Une nouvelle évolution a été l’arrivée de la
programmation procédurale ou structurée (ex. le
langage C au début des années 1970)
Programmation procédurale ou structurée :
Le problème à résoudre est décomposé
récursivement en sous-problèmes jusqu'à
descendre à des actions primitives. Un programme
est ainsi décomposé en un ensemble de sous-
programmes appelés procédures
Un écart important entre les termes utilisés du coté
de programmeurs et ceux utilisés par les
utilisateurs pour exprimer leurs besoins
5
Introduction
Programmation procédurale ou structurée :
La moindre modification des structures de données d’un
programme conduit à la révision de toutes ses procédures
Objet 2 Objet 3
Messages
Objet 1
8
Qu'est ce que la la POO ?
Objectifs de la POO:
9
Qu'est ce que la POO ?
Vocabulaire objet :
Objet
Un objet est une entité, qui possède un état et un comportement :
objet = état + comportement
Classe
Vue de la programmation objet, une classe est un type structuré de
données. Nous verrons qu’une classe C++ est le prolongement des
structures C (mot-clé struct)
Vue de la modélisation objet, une classe correspond à un concept du
domaine modélisé. Une classe regroupe des objets qui ont des
propriétés et des comportements communs
Abstraction
Considérer uniquement les attributs qui sont utiles pour le contexte
étudié 10
Qu'est ce que la POO ?
Vocabulaire objet :
Instance
Pour désigner un objet de la classe, on dit aussi une instance. « instance
» est un anglicisme qui possède une signification proche de celle de «
exemple » en français. On dit souvent qu’une instance « instancie » une
classe. Cela signifie que l’instance est un exemple de la classe.
Attribut ou membre
L’état d’un objet est l'ensemble des valeurs de ses attributs. Un attribut
est une propriété de l’objet.
Message
Les objets « communiquent » en « envoyant » et « recevant » des «
messages ».
envoyer un message à un objet = appeler une méthode associée à l’objet
recevoir un message = entrer dans le corps de la méthode appelée
12
Qu'est ce que la POO ?
Vocabulaire objet :
Encapsulation des données
Les données membres ne sont pas directement accessibles à l'extérieur de
la classe
L'encapsulation est appliquée de manières très diverses suivant les
langages
Le C++ prévoit les mot-clés ‘private’ et ‘public’ pour dire si un attribut est
visible ou non de l'extérieur de la classe
Conséquences :
Un objet n’est vu que par ses spécifications publiques ( son interface)
Une modification interne est sans effet pour le fonctionnement général
du programme
Meilleure réutilisation de l’objet
13
Qu'est ce que la POO ?
Vocabulaire objet :
L’Héritage
Permet de définir les bases d’un nouvel objet à partir d’un objet existant
Le nouvel objet hérite les propriétés de l’ancêtre et peut recevoir de
nouvelles fonctionnalités
Avantages
15
Minimum sur les entrées sorties
Créer le projet hello de type Console Application :
//main.cpp
#include <iostream.h>
void main()
{
cout << "Hello World !";
cout << "Hello World !\n";
cout << "Hello World !" << endl;
int n = 5;
cout << "La valeur est " << n << endl;
float f = 3.14f;
char * ch = "Coucou";
cout << ch << " float = " << f << endl;
}
16
Minimum sur les entrées sorties
Lire sur l’entrée standard
Le programme suivant demande d’entrer une valeur sur l’entrée
standard, puis il la lit avec l’opérateur >> et le stream cin de l’entrée
standard ; enfin il affiche la valeur entrée par l’utilisateur.
#include <iostream.h>
void main () {
int n; float x ; char t[64] ;
cout << “ entrer un entier, un flottant et une chaîne de caractères:” ;
cin >> n >> x >> t ;
cout << “ l’entier vaut ” << n ;
cout << “ le flottant vaut ” << x ;
cout << “ la chaîne vaut ” << t << endl ;
}
De même que pour <<, l’opérateur >> accepte presque tous les
types et il est associatif.
17
Minimum sur les entrées sorties
Les avantages des flux C++ sont nombreux, on notera en
particulier ceux-ci :
le type des donnée est automatiquement pris en compte par les
opérateurs d'insertion et d'extraction (ils sont surchargés pour
tous les types prédéfinis) ;
18
Rappel sur les références et les
pointeurs
Quand on déclare une variable avec un nom et un type,
un emplacement mémoire du type de la variable est créé
à une certaine adresse avec son nom pour y accéder.
x = 3 ; // une affectation
19
Rappel sur les références et les
pointeurs
Le C permet de manipuler dans le programme, les adresses
des emplacements mémoire des variables
On peut déclarer des pointeurs d’un type qui sont des variables
contenant des adresses de variables de ce type avec le
symbole *.
21
Rappel sur les références et les
pointeurs
On peut aussi déclarer une référence à une variable
existante
22
Rappel sur les références et les
pointeurs
Noter que les opérateurs & et * sont inverses l’un de l’autre. On a
toujours :
*(&x) = x et &(*p) = p
27
Emplacement des déclarations
L’emplacement des déclarations est libre en C++. Par contre, on ne
peut utiliser la variable déclarée que dans les instructions du bloc
où est effectuée la déclaration et postérieures à la déclaration.
void f() {
i = 4 ; // incorrect
int i ; // i est déclaré ici
i = 4 ; // correct
{
float f ; // f est declare ici
f = 4.0 ; // correct
} // fin de la portee de f
f = 4.0 ; // incorrect
i = 5 ; // correct
} // fin de la portee de i
28
Arguments par référence
En C, les arguments et la valeur de retour d’une fonction sont transmis par valeur.
Pour simuler en quelque sorte ce qui se nomme ‘‘ transmission par adresse’’ dans
d’autres langages il est nécessaire de ‘‘jongler’’ avec les pointeurs.
#include<iostream>
using namespace std;
void main(){
void echange(int *,int *); avant appel : 10 20
int n=10,p=20; debut echage : 10 20
cout<< " avant appel : " <<n << " " <<p<< "\n "; fin echage : 20 10
echange(&n,&p); apres appel : 20 10
cout<< " apres appel : " <<n << " " <<p<< "\n ";
}
void echange(int *a, int *b){
int c;
cout<<"debut echage : "<<*a<<" "<<*b<<"\n";
c=*a; *a=*b; *b=c;
cout<<"fin echage : "<<*a<<" "<<*b<<"\n";
}
30
Arguments par référence
C++ permet de demander au compilateur de prendre lui-même en charge la
transmission des arguments par adresse
32
Arguments par défaut
Lors d’une déclaration avec des arguments par défaut, ceux-ci doivent être
les derniers de la liste des arguments.
Le programmeur doit fixer les valeurs par défaut dans la déclaration de la
fonction.
33
Surdéfinition de fonction
Un même identificateur peut désigner plusieurs fonctions, à conditions
qu’elles diffèrent par la liste des types de leurs arguments(Surcharge ou en
anglais overloading).
void sosie(int) ;
void sosie(double) ;
main () {
int n = 10 ; double x = 4.0 ;
sosie(n) ;
sosie(x) ;
}
void sosie(int a) {
cout << " sosie avec INT : " << a << endl ;
}
void sosie(double b) {
cout << " sosie avec DOUBLE : " << b << endl ;
}
34
Surdéfinition de fonction
Écrivez la fonction maxi?
void main() {
float x, y, z;
float T[] ={11.1, 22.2, 33.3, 44.4, 7.7, 8.8 };
x = maxi(1.86, 3.14);
y = maxi(1.86, 3.14, 37.2);
z = maxi(6, T);
cout << x <<" "<< y <<" "<< z;
}
35
Surdéfinition de fonction
float maxi(float a, float b) {
return (a > b) ? a : b;
}
Exemple 1
int * ad ;
en C++ on alloue dynamiquement comme cela :
ad = new int ;
alors qu’en C il fallait faire comme ceci :
ad = (int *) malloc (sizeof(int)) ;
Exemple 2
char * adc;
L’instruction : adc = new char[100]; alloue l’emplacement nécessaire pour un
tableau de 100 caractères et place l’adresse (de début) dans adc.
En C le même résultat : adc=(char *) malloc(100);
37
Opérateurs new et delete
Plus généralement, si on a un type donné type, on alloue une variable avec :
new type ;
ou un tableau de n variables avec :
new type [n] ;
En C++, bien que l’utilisation de malloc et free soit toujours permise, il est très
conseillé de n’utiliser que new et delete.
38
Les espaces de noms
Un espace de nom (namespace) est une zone de
déclaration d'identificateurs permettant au
compilateur de résoudre les conflits de noms
namespace first_space{
int N=20 ;
void func(){
cout << "Inside first_space" << endl;
}
} namespace second_space{
int N=30 ;
void func(){
cout << "Inside second_space" <<endl;
}} 39
Les espaces de noms
#include <iostream>
using namespace std;
int main () {
first_space::func();
second_space::func();
cout <<"N of first_space = "<<first_space::N<<endl;
cout <<"N of second_space = "<<second_space::N<<endl;
return 0;
}
40
Les espaces de noms
#include <iostream>
using namespace std;
namespace first_space{
int N=20;
void func(){
cout << "Inside first_space" << endl;
}}
namespace second_space{
int N=30;
void func(){
cout << "Inside second_space" << endl;
}}
using namespace first_space;
int main (){
func();
cout <<"N = "<<N<<endl;
return 0;
} 41
Classes et objets en C++
Déclaration de classes
Définition des fonctions membres
Membres privés ou publiques
Affectation d’objets
Constructeurs
Destructeur
Règles d'utilisation des fichiers .cpp et .h
Membres statiques
42
Déclaration de classes
La déclaration d’une classe est voisine de celle d’une structure. En effet, il
suffit:
De remplacer le mot clé struct par le mot clé class,
De préciser quels sont les membres publics(fonctions ou données) et les
membres privés en utilisant les mots public et private.
class Point {
int x ; // un membre
int y ; // un autre membre
public :
void initialise(int, int) ; // une fonction membre
void deplace(int, int); // encore une
void affiche() ; // encore une fonction membre
};
x et y sont des membres de la classe Point.
initialise, deplace et affiche sont des fonctions membres de la classe Point.
Cet exemple suit le principe d’encapsulation?
43
Définition des fonctions membres
La définition d’une fonction membre suit la syntaxe de la définition d’une fonction C avec
un nom préfixé par le nom de la classe et quatre points ‘::’.
Le symbole ‘::’ s ‘appelle l’opérateur de résolution de portée.
45
Exemple 1
class Point { void Point::affiche() {
int x ; // un membre cout << " x = " <<
int y ; // un autre membre x << endl;
public :
cout << " y = " <<
void initialise(int, int) ; // une fonction
membre y << endl;
void deplace(int, int); // encore une }
void affiche() ; // encore une fonction main() {
membre Point a, b ;
}; a.initialise(5, 2) ;
void Point::initialise(int a, int b) { a.affiche() ;
x = a; a.deplace(8, 4) ;
y = b;
a.affiche() ;
}
void Point::deplace(int dx, int dy) { b.initialise(-1, 1) ;
x += dx; b.affiche() ;
y += dy; }
}
class Toto {
... // des membres prives
public :
... // des membres publiques
private :
... // encore des membres prives
public :
... // encore des membres publiques
... // etc.
};
Si on omet le mot-clé public dans une classe, aucun de ses membres n’est
accessible de l’extérieur de la classe...
47
Constructeurs
Il est souvent nécessaire d’initialiser les objets au moment de leur
création.
Une solution pourrait être de définir pour toutes ces classes une
méthode initialise qui réaliserait les initialisations souhaitées.
48
Constructeurs
Pour résoudre ce problème, C++ possède un mécanisme
d’initialisation automatique d’objets de classe.
49
Destructeur
Le langage C++ offre un mécanisme complémentaire au
constructeur - Une fonction membre particulière pour désinitialiser
les objets :le destructeur.
50
Constructeur et destructeur d’objets
Le but d’un constructeur est :
d’allouer un emplacement mémoire pour l’objet,
d’initialiser les membres de l’objet avec de bonnes
valeurs de départ et
de retourner l’adresse de l’emplacement mémoire
choisi.
Un destructeur d’objet :
remet l’objet dans un état terminal et
libère l’emplacement mémoire associé à l’objet.
51
Constructeur et destructeur d'objets
En fait, notre exemple précédent était approximatif et en bon C++, il
faut écrire :
class Point {
...
public :
Point(int, int) ; // le constructeur de la classe
~Point() ; // le destructeur de la classe
...
};
Point : : Point(int a, int b) {
x=a;
y=b;
}
Point : : ~Point() {} // le destructeur ne sert pas dans cet exemple
52
Constructeur et destructeur d'objets
A partir du moment où un constructeur existe, il n’est pas
possible de créer un objet sans fournir les arguments requis par
le constructeur.
53
Pour comprendre les moments où sont appelés
les constructeurs et destructeurs d’objets?
class Test {
int num ;
La sortie du programme est :
public :
Test(int) ; //déclaration constructeur
++ Appel constructeur - num = 1
~Test() ; //déclaration destructeur
++ Appel constructeur - num = 2
};
Test : :Test(int n) { //définition constructeur -- Appel destructeur - num = 2
num = n ; ++ Appel constructeur - num = 4
cout << "++ Appel constructeur – num = " << num << -- Appel destructeur - num = 4
endl ; -- Appel destructeur - num = 1
} Press any key to continue
Test : :~Test() {
cout << "-- Appel destructeur – num = " << num <<
endl ;
}
void fct(int p) {
Test x(2*p) ;
}
main() {
Test a(1) ;
fct(1) ;
fct(2) ; a est détruit à la sortie du programme main et
} les objets x sont détruits à la sortie de fct. 54
Accesseurs (getters) et
mutateurs (setters)
La notion d'accesseur ou getter :
Un getter est une fonction membre publique permettant de
récupérer la valeur d'une donnée membre protégée (private)
55
Accesseurs (getters) et
mutateurs (setters)
class Personne{
private :
int _age;
public :
int GetAge();
};
int Personne::GetAge(){
return _age;
}
56
Accesseurs (getters) et
mutateurs (setters)
La notion de mutateur ou setter :
Un setter est une fonction membre publique permettant de
modifier une donnée membre protégée(private)
57
Accesseurs (getters) et
mutateurs (setters)
class Personne{
private :
int _age;
public :
void SetAge( int);
};
58
Accesseurs (getters) et
mutateurs (setters)
class Personne{
private :
int _age;
Public :
int GetAge() ;
void SetAge( int);
};
int Personne::GetAge(){
return _age;
}
void Personne::SetAge(int age){
if (age<150) _age=age;
}
59
Règles d'utilisation des fichiers .cpp et .h
Jusqu’ici, nous avions regroupé au sein d’un même programme
trois sortes d’instructions destinées à :
La déclaration de la classe,
La définition de la classe,
L’utilisation de la classe.
60
Règles d'utilisation des fichiers .cpp et .h
Pour l’instant, retenir qu’un fichier classe.h possède la structure suivante :
// fichier classe.h
#ifndef CLASSE_H
#define CLASSE_H
#include ... // includes de classes eventuelles
...
class Classe {
...
};
#endif
61
Règles d'utilisation des fichiers .cpp et .h
Et qu’un fichier classe.cpp possède la structure suivante :
// fichier classe.cpp
#include "classe.h " // include de sa propre classe
#include ... // autres includes optionnels
...
Classe : :Classe(...) { // definition du constructeur
...
}
Classe : :~Classe() { // definition du destructeur
...
}
... // autres definitions de fonctions membres
62
Membres statiques
Avec le qualificatif ‘static’ avant un membre d’une classe on peut spécifier
qu’un membre est commun à tous les objets de la classe.
Objet a Objet b
63
Membres statiques
Une déclaration telle que Exemple a,b;
class Exemple {
Conduit à une situation que l’on peut schématiser ainsi
static int n ;
float x ;
... a.n b.n
};
a.x b.x
Objet a Objet b
64
Membres statiques
Les membres données statiques n’existent qu’en un seul exemplaire,
indépendamment des objets de la classe(même si aucun objet n’a
encore été créé).
class Exemple {
static int n = 2; //erreur
float x ;
...
};
Un membre statique doit être initialisé dans le .cpp tout comme une
fonction membre sinon une erreur sera produite à l’édition de liens:
int Exemple : :n = 2 ; 65
Membres statiques
Écrire une classe compteur permettant d’afficher à tout
moment le nombre d’objet existants?
66
Afficher à tout moment le nombre d’objet existants :
//compteur.h //compteur.cpp
#include <iostream.h>
#ifndef COMPTEUR_H
#include « compteur.h "
#define COMPTEUR_H
class compteur { int compteur::nbre =0;
//User.cpp
#include <iostream.h>
#include « compteur.h "
main(){
void fct();
compteur a;
fct();
compteur b;
}
void fct(){
compteur u, v;
}
68
Propriétés des fonctions membres
Les améliorations concernant les fonctions restent
valables aussi pour les fonctions membres :
surdéfinition de fonctions membres,
arguments par défaut,
objets transmis en argument d'une fonction membre,
par valeur, adresse ou référence.
69
Fonctions membres en ligne
Au lieu de mettre le mot-clef “ inline” devant la définition de la
fonction, on écrit le corps de la fonction au même endroit que sa
déclaration dans la classe.
Ci-dessous elle est inline bien que le mot-clef ne soit pas présent.
class Point { …
void deplace(int dx, int dy) { x += dx; y += dy; };
};
70
Fonctions membres statiques
Une fonction membre statique C++ correspond à une méthode de classe
dans le vocabulaire objet.
Une fonction membre statique s’appelle avec son nom préfixé du nom de la
classe et de ::
71
Autoréférence: le mot-clé this
Quand le programme est dans une fonction membre d’instance, le
programmeur peut manipuler l’adresse de l’instance courante avec le
mot-clef ‘this’.
this n'a évidemment pas de sens dans une fonction membre de classe.
72
Construction, destruction et
initialisation des objets
Les objets automatiques
Les objets statiques
Les objets dynamiques
Initialisation d'un objet lors de sa déclaration
Constructeur par recopie
Objets membres
Initialisation de membres dans l’en-tête d’un constructeur
73
Construction, destruction et
initialisation des objets
En C, une variable peut être créée de deux façons :
74
Les objets automatiques
Les objets automatiques sont les objets déclarés dans une fonction
ou dans un bloc.
void f() {
Truc t ; // t est construit ici
…
{
Bidule b ; // b est construit ici
…
} // b est détruit ici
} // t est détruit ici
75
Les objets statiques
Un objet statique est un objet déclaré avec le mot-clé static dans
une déclaration de classe ou dans une fonction
ou bien à l’extérieur de toute fonction.
76
Les objets dynamiques
Ce sont eux qui font tout l’intérêt de la programmation orienté objet. Un objet dynamique est un
objet créé avec new et éventuellement détruit avec delete.
class point {
int x,y;
public :
point(int abs, int ord) { x=abs; y=ord;
cout << "++ appel constructeur" << endl; }
~point() { cout << "-- appel destructeur" << endl; }
}
main () {
point * adr;
cout << "&& debut main" << endl;
adr = new point(3, 7);
fct(adr);
cout << "&& fin main" << endl;
}
void fct (point * adp) {
cout << "&& debut fct" << endl;
delete adp;
cout << "&& fin fct" << endl;
}
77
Initialisation d'un objet lors de sa déclaration
En C, on peut initialiser une variable au moment de sa déclaration, comme
dans :
int n=12;
En effet, il s’agit de fournir sous une forme peu naturelle des arguments
pour un constructeur.
78
Initialisation d'un objet lors de sa déclaration
D’une manière générale, lorsque l’on déclare un objet avec un initialiseur,
ce dernier peut être une expression de type quelconque à condition qu’il
existe un constructeur à un seul argument de ce type.
79
Constructeur par recopie
Un constructeur par recopie est un constructeur dont la signature est :
NomClasse::NomClasse(NomClasse &)
Son rôle est d’initialiser un objet par recopie d’une autre instance.
Ainsi il est possible de déclarer des objets dont la valeur initiale est une
copie d’un objet créé précédemment :
Point p1(3,4);
Point p2(p1);
Point p3=p1;
Les points p2 et p3 sont initialisés par recopie du point p1.
80
Constructeur par recopie
Si le programmeur ne le définit pas, alors il existe une définition
implicite :
81
Exemple (qui ne marche pas) :
class vecteur {
int nelem ;
double * adr ;
public :
vecteur (int n) { adr = new double [nelem = n] ;
cout << "+++ constructeur usuel +++ adr objet : " << this
<< " +++ adr vecteur : " << adr << "\n" ; }
~vecteur ()
{ cout << "--- Destructeur objet --- adr objet : "
<< this << " --- adr vecteur : " << adr << "\n" ;
delete [ ] adr ;
}
};
main() {
vecteur a(5);
vecteur b(a); // ou bien vecteur b=a ;
}
82
Exemple (qui ne marche pas) :
Lors de la destruction de a, le
destructeur de la classe vecteur
détruit le tableau pointé par adr;
Lors de la destruction de b,
même Chose; a 5
Conclusion, le programme se
peut se planter lors de la
deuxième destruction.
5
b
83
Constructeur par recopie
On peut éviter ce problème avec le constructeur de recopie suivant:
vecteur::vecteur(vecteur & v) {
adr = new double [nelem = v.nelem] ;
for (int i=0; i<nelem; i++)
adr[i] = v.adr[i];
cout << "+++ constructeur recopie +++ adr objet : " << this
<<" +++ adr vecteur : " << adr <<endl;
}
+++ constructeur usuel +++ adr objet : 0x0012FF6C +++ adr vecteur : 0x00491C70
+++ constructeur recopie +++ adr objet : 0x0012FF64 +++ adr vecteur : 0x00491AA0
--- Destructeur objet --- adr objet : 0x0012FF64 --- adr vecteur : 0x00491AA0
--- Destructeur objet --- adr objet : 0x0012FF6C --- adr vecteur : 0x00491C70
Press any key to continue 84
Constructeur par recopie
Les constructeurs ordinaires sont invoqués aux moments de
création des objets
Par contre le constructeur de recopie est invoqué quand un objet
va être dupliquer pour créer un nouvel objet
Lors d'une initialisation avec l'opérateur = (vecteur b=a)
86
Opérateur =
Cependant il reste encore un autre problème !!
Tester l'exécution de votre projet avec le programme pricipal
suivant
int main(){
vecteur a(5) ;
vecteur b(2) ;
b=a;
return 0;
}
87
Opérateur =
Rédefinir l'opérateur = pour pouvoir réaliser une affectation
entre des objets déjà initialisés
88
Opérateur =
Rédefinition de l'opérateur = pour notre classe vecteur :
89
Pour résumer
Constructeur de recopie
int main() { void f(vecteur v){}
vecteur a(5); int main() {
vecteur b=a; vecteur a(5)
return 0; f(a) ;
} return 0;
}
L'opérateur =
int main() {
vecteur a(5);
vecteur b(2);
b=a ;
return 0;
} 90
Forme canonique de Coplien
class T {
public:
T(); // Constructeur par défaut
T(const T &); // Constructeur de recopie
~T (); // Destructeur éventuellement virtuel
T &operator=(const T &); // Operator d'affectation
};
91
Forme canonique de Coplien
Adapter et compléter votre projet pour pouvoir exécuter le
programme suivant : combien d'objets vecteur vont être
créés ?
int main(){
vecteur a(5);
vecteur b=a;
vecteur t[1];
t[0]=a;
return 0;
}
92
Objets membres
Un membre d’un objet peut éventuellement être un objet.
class cercle {
Point centre;
int rayon;
cercle(int, int, int);
};
cercle::cercle(int abs, int ord, int ray) : centre(abs, ord) { … }
Le constructeur de Point est appelé avant celui de cercle.
Pour les destructeurs, c’est l’ordre inverse.
On peut appeler une méthode de la classe Point :
cercle a(1,2,3) ;
a.centre.affiche() ;
93
Initialisation de membres dans l’en-tête
d’un constructeur
La syntaxe que nous avons décrit pour transmettre des arguments à un
constructeur d’un objet membre peut s’appliquer à n’importe quel membre,
même s’il ne s’agit pas d’un objet.
Par exemple:
class point {
int x,y;
public :
Point(int abs=0,int ord=0):x(abs),y(ord){}
…
};
L’appel du constructeur point provoquera l’initialisation des membres x et y.
Le constructeur est vide ici, puisque il n’y a rien de plus à faire pour
remplacer notre constructeur classique:
Point(int abs=0,int ord=0){x=abs;y=ord;}
94
Initialisation de membres dans l’en-tête
d’un constructeur
Cette possibilité peut devenir indispensable Lorsque la donnée
membre :
95
Initialisation de membres dans l’en-tête
d’un constructeur
96
Initialisation de membres dans l’en-tête
d’un constructeur
#include "Cercle.h"
//Cercle.cpp
#include "Point.h"
#include <iostream>
using namespace std;
Cercle::Cercle(Point & p0,int r):centre(p0),rayon(r){
cout << "Constructeur de Cercle avec 2 args "<< endl;
}
Cercle::Cercle(int x0,int y0,int r):centre(x0,y0),rayon(r) {
cout << "Constructeur de Cercle avec 3 args "<< endl;
}
Cercle::~Cercle() {}
void Cercle::deplacer(int dx, int dy){
this->centre.deplacer(dx,dy);
}
void Cercle::afficher(){
cout << "Le rayon = "<< rayon<< " et le centre est ";
this->centre.afficher();
} 97
Initialisation de membres dans l’en-tête
d’un constructeur
#include "Point.h"
//Point.cpp
#include <iostream>
using namespace std;
Point::Point(int x0,int y0):x(x0),y(y0){
cout << "Constructeur de Point "<< endl;
}
Point::Point(Point & p1):x(p1.x),y(p1.y){
cout << "Constructeur de recopie de Point "<< endl;
}
Point::~Point(){}
void Point::afficher(){
cout << "(x = "<< x<< " et y = "<< y<<")"<< endl;
}
void Point::deplacer(int dx,int dy){
x+=dx;
y+=dy;
}
98
Initialisation de membres dans l’en-tête
d’un constructeur
#include <iostream> //main.cpp
#include "Point.h"
#include "Cercle.h"
using namespace std;
int main()
{
Point a(2,2);
Cercle cr(a,5);
Cercle cr2(7,7,2);
//cr=cr2;
return 0;
}
99
Héritage
(relations entre classes)
100
Héritage
Donner à une classe toutes les caractéristiques d’une ou de plusieurs
autres classes (appelées classes mères ou classes de base)
101
Héritage
Considérons par exemple la classe Base suivante :
class Base {
public:
int membreBase;
void SetmembreBase(int valeurBase);
};
Pour déclarer une classse dérivée de la classe Base :
104
Héritage
Constructeurs et héritage
105
Héritage
Héritage et destructeur
106
Héritage
Mise en oeuvre d'un exemple simple : gérer de points colorés
class PointCol : public Point class Point
{ {
public: protected :
PointCol(int,int,short); int x,y;
~PointCol(); public:
PointCol(const PointCol& other); Point(int,int);
PointCol& operator=(const PointCol& other); Point(Point &);
~Point();
private: void afficher();
short couleur; void deplacer(int, int);
}; };
#endif // POINT_H
107
Héritage
Mise en oeuvre d'un exemple simple : gérér de points colorés
PointCol::PointCol(int x1,int y1,short c):Point(x1,y1),couleur(c)
{ cout << "Constructeur de PointCol "<< endl;}
void Point::afficher(){
cout << "mes coordonnées sont (x = "<< x<< " et y = "<< y<<")"<< endl;
}
void Point::deplacer(int dx,int dy){
x+=dx;
y+=dy;
}
109
Héritage
Utilisation des membres de la classe de base
#include <iostream>
#include "PointCol.h"
using namespace std;
int main() {
PointCol a(2,2,1);
a.afficher();
a.deplacer(5,5);
a.afficher();
return 0;
}
110
Héritage
Redéfinition / surcharge des fonctions membre
void PointCol::afficher(){
cout << "PointCol : ma couleur = "<< couleur <<" et ";
Point::afficher();
}
111
Héritage
Après rédefinition de la méthode afficher dans PointCol
#include <iostream>
#include "PointCol.h"
using namespace std;
int main() {
PointCol a(2,2,1);
a.afficher();
a.deplacer(5,5);
a.afficher();
return 0;
}
112
Héritage
Conversion d’un objet dérivé dans un objet de type de base
Point a(4,6);
PointCol b(3,5,2);
a=b;
a.afficher() ;
Conversion de b dans le type Point et affectation du résultat à
a par:
appel de l’opérateur d’affectation de la classe Point s’il a
été surdéfini
par l’emploi de l’affectation par défaut sinon
113
Héritage
Conversion d’un pointeur p sur une classe dérivée en un
pointeur sur une classe de base
Point a(2,2);
PointCol b(4,4,1); Typage statique : le compilateur appelle
Point *p = &a; la méthode qui correspond au type défini
PointCol *pCol=&b; pour la variable
p=pCol;
p->afficher();
#include <iostream>
#include "PointCol.h"
using namespace std;
int main() {
Point a(2,2);
PointCol b(4,4,1);
Point *p = &a;
PointCol *pCol=&b;
p=pCol;
p->afficher();
return 0;
} 115
Héritage
Typage ou ligature dynamique (Polymorphisme)
À l’exécution quand on a un pointeur p qui pointe sur
Point a(4,6);
PointCol b(3,5,2);
Point *p = &a;
PointCol *pCol=&b
p=pCol;
p->afficher()
116
Héritage
Pour utiliser le typage dynamique pour une méthode,
dans la classe de base cette méthode doit être declarée
de type virtual
class Point {
protected:
int x,y;
public:
Point(int,int);
Point(const Point &);
~Point();
virtual void afficher();
void deplacer(int, int);
};
117
Héritage
Typage dynamique
#include <iostream>
#include "PointCol.h"
using namespace std;
int main() {
Point a(2,2);
PointCol b(4,4,1);
Point *p = &a;
PointCol *pCol=&b;
p=pCol;
p->afficher();
return 0;
} 118
Héritage
Polymorphisme : par défaut une méthode en C++ est non
polymorphe
119
Héritage
Quand une méthode est déclarée virtuelle (mot clé
virtual)
Cela précise au compilateur que lors des appels de la
fonction on doit faire une ligature dynamique et non
statique
120
Héritage
Autre situation où la ligature dynamique est indispensable
#ifndef A_H
A::A() { }
#define A_H #ifndef B_H A::~A() { }
#define B_H void A::f(){ g();}
class A {
#include <A.h> void A::g(){cout << " g de
public: A"<<endl;}
A(); class B : public A { B::B() {}
~A(); public: B::~B(){}
B(); void B::g(){cout << " g de B"<<endl;}
void f();
~B(); int main() {
void g();
void g(); B b;
}; }; b.f();
#endif // A_H #endif // B_H return 0;
} 121
Héritage
Autre situation où la ligature dynamique est indispensable
#ifndef A_H
A::A() { }
#define A_H #ifndef B_H A::~A() { }
#define B_H void A::f(){ g();}
class A {
#include <A.h> void A::g(){cout << " g de
public: A"<<endl;}
A(); class B : public A { B::B() {}
~A(); public: B::~B(){}
B(); void B::g(){cout << " g de B"<<endl;}
void f();
~B(); int main() {
virtuall void g();
void g(); B b;
}; }; b.f();
#endif // A_H #endif // B_H return 0;
} 122
Héritage
Seule une fonction membre peut être virtuelle
Redéfinition d’une fonction virtuelle n’est pas obligatoire
Un constructeur ne peut pas être virtuel
Un destructeur peut être virtuel
Fonctions virtuelles pures ( (définition nulle) ) = outil pour
créer une classe abstraite ( une classe qu'on peut pas
instancier )
virtual void surface()=0;