Mon Support Du Cours CPP - FID1 - 2021-2022
Mon Support Du Cours CPP - FID1 - 2021-2022
Mon Support Du Cours CPP - FID1 - 2021-2022
Polycopié du cours
__________________________________
Programmation Orientée Objet
__________________________________
2021-2022
Table des matières
1
Chapitre 1 :
Présentation du langage C++.
2
Le langage C++ a été conçu à partir de 1982 par Bjarne Stroustrup (AT&T Bell
Laboratories). C++ a connu plusieurs versions, jusqu’à sa normalisation par l’ANSI en
1998. D’après Bjarne Stroustrup, conception du langage C++ pour : Être meilleur que C,
Permettre les abstractions de données et permettre la programmation orientée-objet. C++
est un sur-ensemble de C. Certaines des extensions du C++ pourraient en fait être ajoutées
au langage C, sans qu’il soit pour autant « orienté objet ». C++ a introduit de nouvelles
possibilités d’entrées-sorties (basées sur la notion de flot) qui rendent superflues les
fonctions standards de C telles que printf ou scanf. C++ dispose d’opérateurs de gestion
dynamique (new et delete) qui remplacent avantageusement les fonctions malloc, calloc et
free du C. Comme tout langage, C++ dispose d’une bibliothèque standard, c’est-à-dire de
fonctions et de classes prédéfinies. Elle comporte notamment de nombreux patrons de
classes et de fonctions permettant de mettre en œuvre les structures de données les plus
importantes (vecteurs dynamiques, listes chaînées, chaînes...).
1. Les identificateurs
Certains « mots-clés » sont réservés par le langage à un usage bien défini et ne peuvent
pas être utilisés comme identificateurs.
2. Les commentaires
3
Comme tout langage évolué, C++ autorise la présence de commentaires dans vos
programmes source. Il existe deux types de commentaires : les commentaires « libres »,
hérités du langage C et les commentaires de fin de ligne (introduits par C++).
En général, un module objet créé ainsi par le compilateur n’est pas directement
exécutable. Il lui manquera, en effet, au moins les fonctions de la bibliothèque standard
dont il a besoin. La bibliothèque est une collection de modules objets organisée, suivant
l’implémentation concernée, en un ou plusieurs fichiers. Le rôle de l’éditeur de liens que
d’aller rechercher dans la bibliothèque standard les modules objets nécessaires. Le résultat
de l’édition de liens est ce que l’on nomme un programme exécutable, c’est-à-dire un
ensemble autonome d’instructions en langage machine.
4
Tableau 1 : Les types entiers et les types flottants
cout et cin
7. Tableaux
5
Un tableau est une variable qui contient un ensemble de variable de même type.
Les éléments du tableau sont repérés, respectivement par un ensemble d’indices.
Les éléments de ce tableau sont rangés, respectivement en des positions mémoire
successives. C++ utilise la même syntaxe que le langage C pour les tableaux.
8. Les pointeurs
Toute variable manipulée dans un programme est stockée quelque part en mémoire
centrale. Cette mémoire est constituée d'octets qui sont identifiés de manière univoque par
un numéro qu'on appelle adresse. Pour retrouver une variable, il suffit donc de connaître
l'adresse de l'octet où elle est stockée (ou, s'il s'agit d'une variable qui recouvre plusieurs
octets contigus, l'adresse du premier de ces octets). Pour des raisons évidentes de lisibilité,
on désigne souvent les variables par des identificateurs, et non par leur adresse.
Notion de pointeur
Un pointeur est une variable permettant de stocker une adresse-mémoire
Déclaration :
type *pointeur. Où le type est celui des variables auxquelles permet d’accéder le pointeur
Zéro
Zéro est une constante intégral, virgule flottante, ou pointeur. Aucun objet n’est alloué
avec l’adresse 0. Par conséquent, 0 pour un pointeur indique que ce pointeur ne fait pas
référence à un objet.
Arithmétique de pointeurs
L’addition et la soustraction fonctionnent comme avec les adresses…
Opérateur *
Permet d’accéder au contenu d’une variable par un pointeur :
6
int x, y, *p ;
x = 10 ;
p = &x ;
y = *p ; /* y doit contenir 10 */
Fonctionne aussi avec les adresses :
*(tab + 3) est équivalent à tab[3]
9. Pointeur et constante
Constante
Les habitués du C ont l’habitude d’utiliser la directive du préprocesseur #define pour
définir des constantes. Il est reconnu que l’utilisation du préprocesseur est une source
d’erreurs difficiles à détecter. En C++, l’utilisation du préprocesseur se limite aux cas les
plus sûrs : Le mot réservé const permet de définir une constante. Il est possible d’ajouter
le mot clé const à la déclaration d’un objet afin de le rendre constant. Une valeur ne
pouvant pas être attribuée à une constante, celle-ci doit être initialisée.
int y=20;
int x = 10;
const int *px = &x; // pointeur de constante
*px = 6; // erreur: pc pointe sur une constante
px = &y; // ok, pointeur pc n’est pas constant
-------------------------------------------------------------------------------------------------
int *const py = &y; // pointeur constant.
*py = 9; // ok
py = px; // erreur: cp n’est pas constant
-------------------------------------------------------------------------------------------------
const int *const pz = &z; // pointeur const de const
pz = 7; // erreur: cpc pointe sur une constante
pz = px; // erreur: cpc est une constante
char s[ ] = “Gorm”;
const char* pc = s; // pointeur de constante
7
pc[3] = ‘g’; // erreur: pc pointe sur une constante
pc = p; // ok, pointeur pc n’est pas constant
char *const cp = s; // pointeur constant.
cp[3] = ‘a’; // ok
cp = p; // erreur: cp n’est pas constant
const char *const cpc = s; // pointeur const de const
cpc[3] = ‘a’; // erreur: cpc pointe sur une constante
cpc = p; // erreur: cpc est une constante
Vous pouvez attribuer l’adresse d’une variable à un pointeur constant. L’adresse d’une
constante ne peut être attribuée que à un pointeur constant.
int a=1;
const int c=2;
const int* p1=&c; //ok
const int* p2=&a; //ok
int* p3=&c; // erreur: initialisation de int* avec const int*
*p3 = 7; // tente de modifier la valeur de c
Allocation Mémoire
Le C++ met à la disposition du programmeur deux opérateurs new et delete pour remplacer
respectivement les fonctions malloc et free .
(bien qu’il soit toujours possible de les utiliser).
L’opérateur new
L’opérateur delete
L’opérateur new
L’opérateur new réserve l’espace mémoire qu’on lui demande et l’initialise.
Il retourne:
soit l’adresse de début de la zone mémoire allouée,
soit 0 si l’opération à échouée.
L’opérateur new:
int *ptr1, *ptr2, *ptr3;
L’opérateur dedete:
L’opérateur delete libère l’espace mémoire alloué par new à un seul objet, tandis que
l’opérateur delete[] libère l’espace mémoire alloué à un tableau d’objets.
// libération d’un entier
8
delete ptr1;
// libération d’un tableau d’entier
delete[] ptr2;
A chaque instruction new doit correspondre une instruction delete.
Il est important de libérer l’espace mémoire dès que celui ci n’est plus nécessaire.
La mémoire allouée en cours de programme sera libérée automatiquement à la fin du
programme.
11.Références
Variables références
En plus des variables normales et des pointeurs, le C++ offre les variables références. Une
variable référence permet de créer une variable qui est un "synonyme" d'une autre. Dès
lors, une modification de l'une affectera le contenu de l'autre.
int x;
int & y = x; // y est une référence à x
int *ptr;
x=1;
cout << "x= " << x << " y= " << y << endl;
// affichage de : x= 1 y= 1
y=2;
cout << "x= " << x << " y= " << y << endl;
// affichage de : x= 2 y= 2
ptr = &y;
*ptr = 3;
cout << "x= " << x << " y= " << y << endl;
// affichage de : x= 3 y= 3
Une variable référence doit obligatoirement être initialisée et le type de l'objet initial doit être
le même que l'objet référence.
9
Chapitre 2:
Classes et objets
10
La P.O.O basée entièrement sur le concept de classe. Une classe est la généralisation de la
notion de type défini par l’utilisateur, dans lequel se trouvent associées à la fois des
données (membres données) et des méthodes (fonctions membres). En P.O.O. « pure »,
les données sont encapsulées et leur accès ne peut se faire que par le biais des méthodes.
En C++, en revanche, vous pourrez n’encapsuler qu’une partie des données d’une classe
(même si cette démarche reste généralement déconseillée). Vous pourrez même ajouter
des méthodes au type structure (mot clé struct); dans ce cas, il n’existera aucune
possibilité d’encapsulation. Ce type sera rarement employé sous cette forme généralisée
mais comme, sur un plan conceptuel, il correspond à un cas particulier de la classe.
1. Notion de classe
En C++, la structure est un cas particulier de la classe. Une classe est une structure dans
laquelle vous pourrez encapsuler certains membres et/ou fonctions membres. 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 clés public et private.
11
Figure 2 : Exemple 2 d’un programme en C++.
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 ; Dans notre exemple, tous les membres
données de point sont privés, ce qui correspond à une encapsulation complète des
données. Si aucun de ces deux mots « private ou public » n’apparaît au début de la
définition, tout se passe comme si private y avait été placé. Il existe un troisième mot,
protected (protégé), lequel n’intervient que dans le cas de classes dérivées. Il est possible
de rendre certaines fonctions de la classe privées. Dans ce cas, ces fonctions ne seront plus
accessibles de l’extérieur de la classe. Elles ne pourront être appelées que par d’autres
fonctions membres.
2. Affectation d’objets
class point
{
int x ;
public :
int y ;
....
};
point a, b ;
l’instruction :
12
b=a;
provoquera la recopie des valeurs des membres x et y de a dans les membres correspondants
de b.
Contrairement aux ces des structures, il n’est plus possible ici de remplacer cette instruction
par :
b.x = a.x ;
b.y = a.y ;
13
Figure 4 : Exemple 4 d’un programme en C++.
À partir du moment où une classe possède un constructeur, il n’est plus possible de créer
un objet sans fournir les arguments requis par son constructeur (sauf si ce dernier ne
possède aucun argument !). Pour une classe point disposant d’un constructeur sans
argument, la déclaration d’objets de type point peut s’écrire de la même manière que si la
classe ne disposait pas de constructeur :
Lorsqu’une classe ne définit aucun constructeur, tout se passe en fait comme si elle
disposait d’un « constructeur par défaut » ne faisant rien.
14
Figure 5 : Exemple 5 d’un programme en C++.
15
Figure 7: Exemple 7 d’un programme en C++.
16
Chapitre 3 :
Les propriétés des fonctions membres.
17
Nous allons étudier un peu plus en détail l’application aux fonctions membres des
possibilités offertes par C++: surdéfinition, arguments par défaut, fonction en ligne,
transmission par référence...
Le statut privé ou public d’une fonction membre n’intervient pas dans la surdéfinition des
fonctions membres.
18
L’algorithme recherche de la meilleure fonction, et ceci, indépendamment de leur statut
(public ou privé), conclut alors que f(char) est la meilleure fonction et qu’elle est unique.
si f(char) est définie publique, elle serait bien appelée par a.f(c) ;
Dans l’exemple précédent, l’objet pt était transmis classiquement à coincide, à savoir par
valeur. Précisément, cela signifie donc que, lors de l’appel : les valeurs des données de b
sont recopiées dans un emplacement (de type point) local à coincide (nommé pt). En fait,
en C++, n’importe quelle fonction membre d’une classe peut accéder à n’importe quel
membre (public ou privé) de n’importe quel objet de cette classe. On parle dans ce cas de
la transmission par les valeurs.
Transmission par adresse d’un objet
19
Pour ne pas modifier les valeurs des objets, des arguments des fonctions, utilisés en
passage par adresse, il faut utiliser le qualificatif const.
20
Pour ne pas modifier les valeurs des objets, des arguments des fonctions, utilisés en
passage par adresse en référence, il faut utiliser le qualificatif const.
21
Chapitre 4:
Construction, destruction et initialisation
des objets
22
En C++, une variable peut être créée de deux façons : par une déclaration : elle est alors de
classe automatique ou statique ; sa durée de vie est parfaitement définie par la nature et
l’emplacement de sa déclaration ; en utilisant les opérateurs new et delete ; elle est alors
dite dynamique ; sa durée de vie est contrôlée par le programme. Ces trois « classes
d’allocation » (statique, automatique, dynamique) vont naturellement s’appliquer aux
objets.
1. Durée de vie
Les objets automatiques sont ceux créés par une déclaration :
Dans une fonction, L’objet est créé lors de la rencontre de sa déclaration et Il est
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 il est
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, ou bien 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.
En ce qui concerne la chronologie, on peut dire que : le constructeur est appelé après la
création de l’objet et le destructeur est appelé avant la destruction de l’objet.
C++ fait du constructeur (dès lors qu’il existe) un passage obligé lors de la création d’un
objet. Après l’allocation dynamique de l’emplacement mémoire requis, l’opérateur new
appellera un constructeur de l’objet. Pour que new puisse appeler un constructeur
disposant d’arguments, il est nécessaire qu’il dispose des informations correspondantes.
Avant la libération de l’emplacement mémoire correspondant, l’opérateur delete appellera
le destructeur.
24
Chapitre 5:
Le constructeur de recopie
25
Nous avons vu comment C++ garantissait l’appel d’un constructeur pour un objet créé par
une déclaration ou par new. Mais il existe des situations dans lesquelles il est nécessaire
de construire un objet, même si le programmeur n’a pas prévu de constructeur pour cela.
Le cas de la valeur d’un objet transmise en argument à une fonction. le cas d’un objet
renvoyé par valeur comme résultat d’une fonction le cas où un objet est initialisé, lors de
sa déclaration, avec un autre objet de même type.
26
3. Objet est initialisé, lors de sa déclaration, avec un autre objet de
même type.
point a;
Ces deux déclarations (point b=a et point b(a)) entraînent effectivement la création d’un
objet de type point, suivie de l’appel du constructeur par recopie
D’une manière générale, on regroupe ces trois situations sous le nom d’initialisation par
recopie. Une initialisation par recopie d’un objet est donc la création d’un objet par
recopie d’un objet existant, de même type. Pour réaliser une telle initialisation, C++ a
prévu d’utiliser un constructeur particulier dit constructeur de recopie. Si un tel
constructeur n’existe pas, un traitement par défaut est prévu; on peut dire, de façon
équivalente, qu’on utilise un constructeur de recopie par défaut. Dans toute situation
d’initialisation par recopie il y toujours appel d’un constructeur de recopie.
À la fin de l’exécution de la fonction fct, le destructeur ~point est appelé pour b, ce qui
libère l’emplacement pointé par adr. A la fin de l’exécution de la fonction main, le
destructeur est appelé pour a, ce qui libère... le même emplacement. Cette tentative
constitue une erreur d’exécution dont les conséquences varient avec l’implémentation.
dont nous savons qu’il sera appelé dans toute situation d’initialisation donc, en particulier,
lors de l’appel de fct.
27
Vous pouvez fournir explicitement dans votre classe un constructeur de recopie. Il doit
alors s’agir d’un constructeur public disposant d’un seul argument du type de la classe et
transmis obligatoirement par référence.
Exemple:
On notera que si ce constructeur de recopie est privé, il n’est appelable que par des
fonctions membres de la classe.
Notez bien que C++ impose au constructeur par recopie que son unique argument soit
transmis par référence (sinon l’appel du constructeur de recopie impliquerait une
initialisation par recopie de l’argument).
Il doit alors s’agir d’un constructeur disposant d’un seul argument du type de la classe et
transmis obligatoirement par référence.
28
Chapitre 6:
Les fonctions amies
29
En C++, le principe d’encapsulation interdit à une fonction membre d’une classe
d’accéder à des données privées d’une autre classe. Supposons que vous avez deux
classes, une nommée vecteur et l’autre nommée matrice. Il est possible que vous
souhaiterez alors définir une fonction permettant de calculer le produit d’une matrice par
un vecteur. Bien entendu, vous pourriez toujours rendre publiques les données de vos
deux classes, mais vous perdriez alors le bénéfice de leur protection. En C++, la notion de
fonction amie propose une solution intéressante, sous la forme d’un compromis entre
encapsulation formelle des données privées et des données publiques.
Lors de la définition d’une classe, il est possible de déclarer qu’une ou plusieurs fonctions
(extérieures à la classe) sont des « amies ». Une telle déclaration d’amitié les autorise
alors à accéder aux données privées, au même titre que n’importe quelle fonction membre.
Il existe plusieurs situations d’amitiés : fonction indépendante, amie d’une classe ;
fonction membre d’une classe, amie d’une autre classe ; fonction amie de plusieurs classes
; toutes les fonctions membres d’une classe, amies d’une autre classe.
30
Chapitre 6:
L’Héritage
31
Le concept d’héritage constitue l’un des fondements de la P.O.O. L’héritage vous autorise
à définir une nouvelle classe, dite « dérivée », à partir d’une classe existante dite « de
base ». La classe dérivée « héritera » des « potentialités » de la classe de base, tout en lui
en ajoutant de nouvelles, et cela sans qu’il soit nécessaire de remettre en question la classe
de base. Une classe dérivée peut devenir à son tour classe de base pour une autre classe.
C++ autorise l’héritage multiple, grâce auquel une classe peut être dérivée de plusieurs
classes de base.
1. Syntaxe de l’héritage
Héritage simple :
Héritage multiple :
lors de la destruction d’un objet de type B, il y aura: appel du destructeur de B, puis appel
de celui de A (les destructeurs sont appelés dans l’ordre inverse de l’appel des
constructeurs).
32
public: pointcol (int, int, char) ;
};
et que l’on souhaite que pointcol retransmette à point les deux premières informations
reçues, on écrira son en-tête de cette manière :
pointcol (int abs, int ord, char cl) : point (abs, ord)
Le compilateur mettra en place la transmission au constructeur de point des informations
abs et ord correspondant (ici) aux deux premiers arguments de pointcol.
Ainsi, la déclaration : pointcol a (10, 15, 3) ;
entraînera :
33
Il est possible d’utiliser le mécanisme de transmission d’informations entre constructeurs.
Dans ces conditions, on voit que ce constructeur doit recevoir en argument non pas l’objet
x tout entier, mais seulement ce qui, dans x, est de type A.
C’est là qu’intervient la possibilité de conversion implicite d’une classe dérivée dans une
classe de base
34
35