Explorer les Livres électroniques
Catégories
Explorer les Livres audio
Catégories
Explorer les Magazines
Catégories
Explorer les Documents
Catégories
ESATIC
Ecole Spérieure Africaine des TIC
UP Informatique
1
INTRODUCTION A LA PROGRAMMATION C++
Objectifs :
1. Apprendre à programmer de façon plus concise et modulaire mais aussi plus fiable et élégante
en exploitant les concepts orientés-objets
2. Approfondir quelques notions de structuration des données (algorithmique, généricité.)
Historique du langage C++ :
- Extension objet du langage C, mis au point par Ritchie et Kernighan dans les années 70’.
- Développé initialement par Bjarne Stroustrup (bell labs) dans les années 80’.
- Normalisé ISO (International Organization for Standardization) en 1998 puis 2003.
Origines de la POO
• La programmation orientée objet surgit dans les années '80
• Logiciels de plus en plus complexes, travail en équipe
• Contrôler la complexité des logiciels
• Code réutilisable pour contenir les coûts du développement
• Contenir les coûts de la maintenance :
– Ajout de nouvelles fonctionnalités
– Modification de fonctionnalités existantes
– portage sur d'autres plate-formes ou environnements
• Coordonner et répartir le travail de développement
• Modularité
#include
En C++,<iostream>
les fichiers sources se terminent par
l’extension
Using namespace std.cpp
.cc ou ; au lieu de .c en langage C.
Main() {
Cout << "hello world !" << endl ;
}
3
Comme en C, si on ne veut pas qu’une variable soit modifiable : la définir comme constante par
l’indication du mot réservé const lors de la déclaration :
Exemples : int const couple(2); double const g(9.81); const int model = 90 ; const int v[ ] = {1, 2, 3,
4} ; // v[i] est une constante
Une fois déclarée, une constante ne pourra donc plus être modifiée par le programme (toute tentative
de modification produira un message d’erreur lors de la compilation).
3. Affectation :
L’opération d’application affecte une valeur à une variable.
Exemples :
int val(2) ;
double pi(3.1415) ;
char c(‘a’) ;
int j(2*i+5) ;
5. Types élémentaires :
Les entiers : int, short int, long int, unsigned int, …
Les réels : double (double précision)/float (simple précision),
Les caractères : char,
Les booléens : bool.
6. Opérateurs et expressions :
o Les opérateurs arithmétiques : *, /, %, +, -
o C++ fournit un certain nombre de notations abrégées pour des affectations
particulières.
x=x+y peut aussi s’écrire x+=y (idem pour -, *, / et %)
x=x+1 peut aussi s’écrire ++x (idem pour -. Exemple : --x)
o Les opérateurs de comparaison : ==, !=, <, >, <=, >=.
o Les opérateurs logiques : && (et), || (ou), ^ (ou exclusif), ! (négation) .
4
7. Entrée – sortie de base :
écriture :
lecture : #include <iostream>
#include <iostream> Using namespace std ;
Using namespace std ; int main() {
int main() { // ecriture
int x ; int x ;
double y ; double y ;
cin >> x >>y ; //lecture cout <<" un entier : "<< n <<"un
return 0 ; double :<<(12.5+3.0) << endl ;
} return 0 ;
}
#include <iostream>
using namespace std;
int main()
{
int i;
cout <<"Tapez une valeur entre 0 et 20 bornes incluses : ";
cin>>i;
while (i<0 || i>20)
{
cout <<"ERREUR ! ";
cout <<"Tapez une valeur entre 0 et 20 bornes incluses : ";
cin >> i;
}
return 0;
}
Explications
o On saisit une première fois la valeur de i par un "cin".
o Tant que la valeur saisie ne sera pas comprise entre 0 et 20 (bornes incluses), on
affiche ERREUR et on redemande une nouvelle valeur.
o La condition « la valeur de i n'est pas comprise entre 0 et 20 » s’écrit ( i<0 || i>20 ).
o Ne pas confondre le ET et le OU logique !
5
cout<<"Tapez un entier : "; cin>>a;
if ((a>=56)&&(a<=78)) cout<<"GAGNE"<<endl; else cout<<"PERDU"<<endl;
return 0;
}
int main()
{
int i;
for (i=8;i<=23;i++) cout<<i<<endl;
return 0;
}
Exemple 2:
Ecrire un programme qui demande à l'utilisateur de taper un entier N et qui calcule la somme des
cubes de 5^3 à N^3.
#include<iostream>
using namespace std;
int main()
{
int N,s=0,i;
return 0;
}
#include<iostream>
using namespace std;
int main()
{
int x=0,choix;
do {
cout<<"x vaut "<<x<<endl;
cout<<"1 : Ajouter 1"<<endl;
cout<<"2 : Multiplier par 2"<<endl;
cout<<"3 : Soustraire 4"<<endl;
cout<<"4 : Quitter"<<endl;
cout<<"Votre choix : "; cin>>choix;
switch(choix)
{
case 1: x++; break;
case 2: x=x*2; break;
case 3: x=x-4; break;
}
}while(choix!=4);
return 0;
}
7
LA PROGRAMMATION ORIENTEE OBJET (POO)
Objectifs pédagogiques:
- Introduire les notions d’encapsulation, d’abstraction, d’objets, d’instances et de classes
- Classes en C++
- Variables d’instance
- Méthodes d’instance
- Encapsulation et interface (public : et private :)
- L’objet this et le masquage
I. Avantages de la POO :
Façon naturelle de modéliser les données et traitements intervenant dans un programme.
Modularité : le programme est composé d’un ensemble d’entités (types d’objets) aux rôles
bien déterminés.
Réutilisabilité : chaque type d’objets peut être réutilisé, sa sémantique peut être étendue par le
biais de l’héritage.
Polymorphisme : un même code peut être s’appliquer à des types d’objets différents.
Abstraction : la représentation des données est découplée de leur utilisation. De plus, des
règles précises d’utilisation des données peuvent être imposées.
D’énormes librairies déjà écrites (Java, C#). Ce qui permet une efficacité dans la conception
des programmes, concision du codage, meilleures fiabilité et maintenabilité des programmes
Un logiciel fiable, souple et facile à maintenir est difficile à otenir, mais sera plus facile avec la POO.
1. Notion d’encapsulation :
Le principe d’encapsulation consiste à regrouper dans le même objet informatique
« concept », données et traitements qui lui sont spécifiques :
o Les données incluses dans un objet seront appelées les attributs de cet objet,
o Les traitements/fonctions défini(e)s dans un objet seront appelées les méthodes de cet
objet.
Les objets sont définis par leurs attributs et leurs méthodes : OBJET= attributs + méthodes
8
Ceci permet de faire une description générique de l’ensemble considéré : se focaliser sur
l’essentiel, cacher les détails.
Les données sont manipulées à l’aide de méthodes qui cachent leur représentation interne :
o On peut imposer que ces méthodes vérifient l’intégralité des données (fiabilité).
o L’utilisation d’une donnée ne dépend plus des choix faits pour sa représentation
(facilité de maintenance).
o Des données différentes peuvent être manipulées de manière identique (concision)
EXEMPLE : Rectangle
La notion d’objet rectangle n’est intéressante que si l’on peut lui associer des propriétés
et/ou mécanismes généraux. On s’intéresse ainsi aux propriétés et mécanismes valables
pour l’ensemble des rectangles et non pas pour un rectangle particulier.
Les notions de largeur et hauteur sont des propriétés générales des rectangles (attributs).
Le mécanisme permettant de calculer la surface d’un rectangle (surface=largeur * hauteur)
est commun à tous les rectangles (méthodes).
Un intérêt de l’encapsulation est que cela permet d’abstraire :
En plus du regroupement des données et des traitements relatifs à une entité, l’encapsulation permet
en effet de définir deux niveaux de perception :
Le niveau externe : partie visible par les programmeurs-utilisateurs de l’objet (c’est-à-dire le
prototype des méthodes et attributs hors de l’objet. C’est l’interface.
Le niveau interne : détails d’implémentation de l’objet :
o Méthodes et attributs accessibles uniquement depuis l’intérieur de l’objet(ou
l’intérieur d’objets similaires).
o Définition de l’ensemble des méthodes de l’objet. C’est le corps de l’objet.
Rectangle
Classe (type abstrait)
Attributs : Méthodes :
largeur dessine()
hauteur surface()
Existence conceptuelle
(Écriture du programme)
Existence concrète
(Exécution du programme)
h : 15 h : 30
h : 30
l : 26 l : 30
l : 12
instance2 instance3
instance1 9
4. Pourquoi abstraire/encapsuler ?
L’intérêt de regrouper les traitements et les données conceptuellement reliés est de permettre une
meilleure visibilité et une meilleure cohérence au programme, d’offrir une plus grande modularité
L’intérêt de séparer les niveaux interne et externe est de donner un cadre plus rigoureux à
l’utilisation des objets utilisés dans un programme.
Les objets ne peuvent être utilisés qu’au travers de leurs interfaces (niveau externe) et donc les
éventuelles modifications de la structure interne restent invisibles à l’extérieur. Même idée que la
séparation prototype/définition d’une fonction.
Règle du masquage : les attributs d’un objet ne doivent pas être accessibles depuis l’extérieur, mais
uniquement par des méthodes.
5. Encapsulation et interface :
Il y a donc deux facettes à l’encapsulation :
1er : regroupement de tout ce qui caractérise l’objet : données (attributs) et traitements (méthodes).
2e : isolement et dissimulation des détails d’implémentation interface.
Interface = ce que le programmeur-utilisateur (hors de l’objet) peut utiliser.
Tout ceci permet la concentration sur les attributs/méthodes concernant l’objet (abstraction).
Tout ce qui n’est pas nécessaire de connaitre à l’extérieur d’un objet devrait être dans le corps de
l’objet et identifié par le mot-clé « private : ». À l’inverse, l’interface qui est accessible de
l’extérieur, se déclare avec le mot-clé « public : ».
NB : si aucun droit n’est précisé, c’est private par défaut.
Exemple :
class Rectangle {
public : // accessible partout
double surface () const {…}
private : // accessible uniquement ici
double hauteur ;
double largeur ;
…
};
10
III. LES CLASSES EN C++ :
En C++, une classe se déclare par le mot-clé class.
Exemple :
class Rectangle {
…
} ; //ne pas oublier le ;
La déclaration d’une instance d’une classe se fait de la façon similaire à la déclaration d’une variable
classique : nom_classe nom_instance ;
Exemple : Rectangle rect1 ; // déclare une instance rect1 de la classe Rectangle.
1. Déclaration des attributs :
La syntaxe de la déclaration des attributs est la même que celle des champs d’une Structure :
type nom_attribut ;
Exemple : les attributs hauteur et largeur, de type double, de la classe Rectangle pourront être
déclarés comme suit :
Class Rectangle {
double hauteur ;
double largeur ;
…};
class Rectangle {
…
double surface () {
return (hauteur*largeur) ;
}
};
11
REMARQUE : à noter que ce n’est pas parce qu’on n’a pas besoin de passer les valeurs des
attributs de la classe comme arguments aux méthodes de cette classe, que les méthodes n’ont jamais
d’arguments.
Les méthodes peuvent très bien avoir des arguments : ceux qui sont nécessaires (et donc extérieurs
à l’instance) pour exécuter la méthode en question.
Exemple :
class couleur {…} ;
class Figurecoloree {
// …
Void coloree (couleur) {…}
}
Figurecoloree une_figure ;
Couleur rouge ;
// …
Une_figure.coloree (rouge) ;
//…
1. Actions et prédicats :
En C++, on peut distinguer les méthodes qui modifient l’état de l’objet (appelées actions) de celles
qui ne changent rien à l’objet (appelées prédicats).
On peut pour cela ajouter le mot-clé const après la liste des arguments de la méthode :
Type_retour nom_méthode (type_arg1 nom_arg1, …) const
Exemple :
class Rectangle {
…
double surface () const {
return (hauteur*largeur) ;
}
…
};
12
2. Méthodes « get » (accesseurs) et « set » (manipulateur) :
Tous les attributs sont privés. Et si on a besoin de les utiliser depuis l’extérieur de la classe ?
Si le programmeur le juge utile, il inclut les méthodes publiques nécessaires…
1er : manipulateurs (méthodes set) :
o Modification
o Affectation de l’argument à une variable d’instance précise
void setHauteur (double h) {hauteur = h ;}
void setLargeur (double l) {largeur = l ;}
2e : accesseurs (méthodes get) :
o consultation
o Retour de la valeur d’une variable d’instance précise
double getHauteur () const {return hauteur;}
double getLargeur () const {return largeur;}
3. Masquage (shadowing) :
Le masquage consiste au faite qu’un identificateur cache un autre identificateur.
Situation typique : le nom d’un paramètre cache un nom d’attribut.
Void setHauteur (double hauteur) {
hauteur=hauteur ;
}
Si dans une méthode, un attribut est masqué alors la valeur de l’attribut peut quand même être
référencée à l’aide du mot-clé this. « This » est un pointeur sur l’instance courante.
La syntaxe pour spécifier un attribut en cas d’ambiguïté est : this->nom_attribut ;
Exemple : l’utilisation de this est obligatoire dans les situations de masquage.
void setHauteur (double hauteur){
This->hauteur=hauteur ;
}
4. Opérateur « :: » :
Il est possible d’écrire les définitions des méthodes à l’extérieur de la déclaration de la classe. Ceci
pour une meilleure lisibilité du code, la modularité.
Pour relier la définition d’une méthode à la classe pour laquelle est définie, il suffit d’utiliser
l’opérateur « :: » de résolution de portée.
La déclaration de la classe contient les prototypes des méthodes
Les définitions correspondantes spécifiées à l’extérieur de la déclaration de la classe, se font sous la
forme : typeRetour Nomclasse :: nomFonction(arg1, arg2, …) {…}
13
EXEMPLE COMPLET DE CLASSE (1):
#include<iostream>
using namespace std;
// définition de la classe
class Rectangle {
public :
// définition des méthodes
double surface () const {return (hauteur*largeur) ;}
double getHauteur () const {return hauteur ;}
double getLargeur () const {return largeur ;}
void setHauteur (double hauteur) {this->hauteur=hauteur ;}
void setLargeur (double largeur) {this->largeur=largeur ;}
private :
// déclaration des attributs
double hauteur ;
double largeur ;
};
// utilisation de la classe
int main() {
Rectangle rect ;
double lu ;
cout<<"Quelle hauteur ?" ; cin>>lu ;
rect.setHauteur (lu) ;
cout<<"Quelle largeur ?" ; cin>>lu ;
rect.setLargeur (lu) ;
cout<< "surface = "<<rect.surface()<<endl ;
return 0 ;
}
14
EXEMPLE COMPLET DE CLASSE (2):
La déclaration de la classe Rectangle pourrait être (dans le fichier rectangle.h).
#include<iostream>
using namespace std;
// définition de la classe
class Rectangle {
public :
// prototypes des méthodes
double surface () const ;
double hauteur () const ;
double largeur () const ;
void hauteur (double) ;
void largeur (double) ;
private :
// déclaration des attributs
double hauteur_ ;
double largeur_ ;
};
Accompagné des définitions « externes » des méthodes (dans un fichier rectangle.cc)
double Rectangle :: surface () const
{
return (hauteur_*largeur_) ;
}
double Rectangle :: hauteur () const
{return hauteur_ ;}
double Rectangle :: largeur () const
{return largeur_ ;}
void Rectangle :: hauteur (double h)
{hauteur_=h;}
void Rectangle :: largeur (double l)
{largeur_=l ;}
// utilisation de la classe
int main() {
Rectangle rect ;
double lu ;
cout<<"Quelle hauteur ?" ; cin>>lu ;
rect.hauteur (lu) ;
cout<<"Quelle largeur ?" ; cin>>lu ;
rect.largeur (lu) ;
cout<< "surface = "<<rect.surface()<<endl ;
return 0 ;
}
15
CONSTRUCTEUR/DESTRUCTEUR
ET LES SURCHARGES
Objectifs :
- Présenter les notions de constructeur et destructeur.
- aborder une des facilités d’écriture du : la surcharge des opérateurs.
INTRODUCTION
Problématique :
Nous avons vu comment déclarer une classe et par la suite, comment déclarer une instance de la
classe. Exemple : Rectangle rect ; Comment peut-on initialiser (attribuer des valeurs) les attributs de
rect ?
Comment faire l’initialisation des attributs ?
1ère solution : affecter individuellement une valeur à chaque attribut.
Rectangle rect ;
double lu ;
cout << "Quelle hauteur ? " ; cin >> lu ;
rect.setHauteur(lu) ;
cout << "Quelle largeur ? " ; cin >> lu ;
rect.setLargeur(lu) ;
En fait, C++ fait déjà le travail pour vous en fournissant des méthodes particulières appelées
constructeurs. Un constructeur réalise toutes les opérations requises en « début de vie » de l’objet.
16
I. LES CONSTRUCTEURS
1. Définition :
Un constructeur d’objet est une méthode :
Invoquée automatiquement lors de l’instanciation d’un objet,
Assurant l’initialisation des attributs,
Les principales caractéristiques d’un constructeur à prendre en compte lors de son utilisation sont :
- Il porte le même nom que la classe dans laquelle il est placé.
- C’est la première fonction membre à être exécutée.
- Il peut posséder des arguments.
- Il n’a pas de type de retour (pas même void).
- Sa définition n’est obligatoire que si elle est nécessaire au bon fonctionnement de la classe.
17
4. Constructeur par défaut par défaut :
Si aucun constructeur n’est spécifié, le compilateur génère automatiquement une version minimale
du constructeur par défaut (c’est donc un constructeur par défaut par défaut) qui :
Appelle le constructeur par défaut des attributs objets;
Laisse non initialisés les attributs de type de base.
Dès qu’au moins un constructeur a été spécifié, ce constructeur par défaut par défaut n’est plus fourni.
Si donc on spécifie un constructeur sans spécifier de constructeur par défaut, on ne peut plus construire
d’objet de cette classe sans les initialiser (ce qui est voulu !) puisqu’il n’y a plus de constructeur par
défaut.
18
Commentaires :
Dans l’exemple précédent, la fonction « constructeur » initialise l’objet P en affectant les valeurs
spécifiées à ses données membres.
Quand la déclaration de la classe P a lieu, le constructeur est appelé automatiquement et la conversion
de la hauteur et du côté de la pyramide, de pouces en centimètres, est effectuée.
Le langage C++ offre le moyen d’initialiser les données membres d’un objet par l’intermédiaire de
listes d’initialisation de constructeurs. Des valeurs par défaut sont choisies, si aucun appel n’est
passé lors de l’appel de la fonction membre.
5. Constructeur de recopie :
C++ vous fournit également un moyen de créer la copie d’une instance : le constructeur de copie.
Ce constructeur permet d’initialiser une instance en utilisant les attributs d’une autre instance de
même type.
Syntaxe :
NomClasse (NomClasse const& obj): …
{ …}
Exemple :
Rectangle (Rectangle const& obj):hauteur (obj.hauteur), largeur (obj.largeur){}
L’invocation du constructeur de copie se fait par une instruction de la forme :
Rectangle r1(12.3, 24.5) ;
Rectangle r2(r1) ;
r1 et r2 sont deux instances distinctes mais ayant des mêmes valeurs pour leurs attributs (à ce
moment-là du programme).
19
int main(){
//création d’un objet T1 instancié depuis la classe trapeze
//petite base b : 15cm, grande base B : 20cm, hauteur h: 6,5cm
trapeze T1(15, 20, 6.5);
//creation d’un objet T2 copie du constructeur T1
trapeze T2(T1);
//affichage de la surface calculée en cm2
cout<<"Surface T1 : "<<T1.surface()<<endl;
//affichage de la surface calculée en pouces2 via le constructeur par copie
cout<<"Surface T2 : "<<T2.surface()<<endl;
return 0;
}
Définition : Un destructeur est une fonction membre qui porte le même nom que la classe. À
la différence d’un constructeur il est précédé du symbole ~ (tilde). Son rôle est de détruire un
objet avant que la mémoire soit libérée. Le destructeur est invoqué automatiquement en fin de
vie de l’instance.
21
Lorsque certains attributs de la classe sont des pointeurs.
Exemple :
Soit une autre définition (farfelue, mais possible !) de la classe Rectangle :
class Rectangle {
private:
double* largeur; // aie, un pointeur !
double* hauteur;
public:
Rectangle(double l, double h): largeur(new double(l)), hauteur(new double(h)) {}
~Rectangle() { delete largeur; delete hauteur; }
double getLargeur() const;
double getHauteur() const;
...
};
Que se passe-t-il lorsqu’on invoque la fonction suivante ?
void afficher_largeur(Rectangle tmp) {
cout << "Largeur: " << tmp.getLargeur() << endl;
}
void afficher_largeur(Rectangle tmp) { // une copie !..
cout << "Largeur: " << tmp.getLargeur() << endl;
} // destruction de tmp...
// constructeur de copie
Rectangle::Rectangle(const Rectangle& obj)
: largeur(new double(*(obj.largeur))),hauteur(new double(*(obj.hauteur)))
{}
// destructeur
void Rectangle::~Rectangle() {
delete largeur;
delete hauteur;
}
22
Le mécanisme du destructeur est automatiquement appélé lorsqu’un objet de la classe sort de la
portée en cours ou que l’opérateur de supression (delete) est appliqué au pointeur de la classe. Le
mécanisme appelle d’abord la fonction destructeur. Après son exécution, le mécanisme libère la
mémoire associée à cet objet. Un objet créé à partir de l’opérateur new restera cependant toujours
accessible. Il devra donc etre explicitement supprimé.
Exemple :
class Personne {
public:
Personne(int ag, char sex)
{age=ag ; sexe=sex ;} //définition inline de méthode
Personne(){age=0 ; sexe=’b’ ;}
~Personne();
private:
int age; char sexe;
};
// code dans la méthode appelante
main()
{Personne *Ange = new Personne(22, ‘M’) ;
Personne Marie(35,’F’) ;
Personne *Katie=new Personne(50,’F’) ;
Personne Jean ;
Jean=Personne(19,’M’) ;
// Destruction des objets
delete Ange, Katie ; //tous les pointeurs
// les objets Marie et Jean seront supprimés lorsque la fonction sortira de la portée
courante
}
NB :
Allocation dynamique. L’opérateur new() permet d’allouer dynamiquement l’espace
mémoire nécessaire pour une (ou plusieurs) instance(s) de classe donnée, de la même
manière que pour les types standard :
Etudiant* pe=new Etudiant; pour un seul exemplaire
Etudiant* pe=new Etudiant[10]; pour un tableau de 10 étudiants.
La désallocation se fait par l’opérateur delete ou delete[]
Accès aux membres. Soit e une instance de classe Etudiant déclarée par
Etudiant e;
1.4.1. L’opérateur ".". Permet un accès direct aux attributs ou méthodes de l’instance :
cout <<e.nom; accès à l’attribut nom
e.calculNoteFinale(); exécution de la méthode calculNoteFinale
L’opérateur "->". Permet l’accès aux attributs ou méthodes de l’instance via un pointeur.
Par exemple, si un pe est un pointeur sur Etudiant,
. Etudiant* pe = &e;
alors on accède aux membres de e par pe->xxx qui équivaut à e.xxx ou encore à (*pe).xxx :
cout <<pe->nom;
pe->calculNoteFinale();
23
III. LA SURCHARGE
1- Surcharge de fonctions membres
De la même façon que pour les fonctions habituelles, il est possible de surdéfinir les fonctions membres
d’une classe. On parle alors de polymorphisme : une même fonction peut avoir plusieurs formes selon
le contexte (l’objet) d’appel.
Exemple 1 : fichier « Point.hpp »
#incude <string>
class Point {
private:
...
public:
...
void Afficher() const;
void Afficher(const string & message) const;
};
Fichier « .cpp »
#include "Point.hpp"
void Point::Afficher(){
cout << "x = " << coord_x << " y = " << coord_y << endl;
}
void Point::Afficher(const string & message){
cout << message << endl;
cout << "x = " << coord_x << " y = " << coord_y << endl;
}
La manœuvre consistant à surcharger une méthode revient à en créer une nouvelle, dont la signature se
différencie de la précédente, uniquement par la liste ou la nature des arguments.
Exemple 2 :
class MaClasse1 {
void maMethode( ){
attribut1 = attribut1 + 1 ;
....
}
void maMethode( int a ){
attribut1 = attribut1 + a ;
....
}
void maMethode( int a, int b ){
attribut1 = a + b ;
....
}
int maMethode( int a, int b ){ /* Il est interdit de définir cette méthode, présentant la même
signature, mais un type de retour différent de la précédente */
}
}
24
2- Surcharge d’opérateurs
Il est pratique de pouvoir utiliser les opérateurs classiques pour effectuer des opérations sur des objets
(par exemple des objets mathématiques : somme de vecteurs, produits de matrices, ...).
Le C++ permet de surcharger la plupart des opérateurs du C :
+ - * / += -= ... ++ --
== < > <= >= ...
[] ()
new, delete
l’affectation =
L’avantage de la surcharge d’opérateurs est de pouvoir réutiliser les dénominations “naturelles” de
ceux-ci.
class Point {
private:
...
public:
...
Point operator+(const Point & p) const;
bool operator==(const Point & p) const;
};
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#include "Point.hpp"
Point Point::operator+(const Point & p){
Point res;
res.coord_x = coord_x + p.coord_x;
res.coord_y = coord_y + p.coord_y;
return res;
// return Point(coord_x + p.coord_x, coord_y + p.coord_y)
}
bool Point::operator==(const Point & p){
if ((coord_x == p.coord_x)
&& (coord_y == p.coord_y)){
return true;
} else {
return false;
}
// return ((coord_x == p.coord_x) && (coord_y == p.coord_y));
}
25
Exemple : RECTANGLES
#include <iostream>
using namespace std;
//définition de la classe rectangle
class rectangle{
public:
float longueur, largeur
//constructeur
rectangle(double L=0, double l=0){
longueur=L;
largeur=l;}
//constructeur par copie
//il est précisé ici, mais n’est pas obligatoire
//puisque créé de façon automatique
//par le compilateur
rectangle(const rectangle&);
//fonction membre affiche
void affiche(){
cout<<"Longueur rectangle rect3 : "<<longueur<<endl;
cout<<"Largeur rectangle rect3 : "<<largeur<<endl;}
};
//surcharge de l’opérateur +
rectangle operator+(rectangle& r1, rectangle& r2){
rectangle R(r1.longueur+r2.longueur, r1.largeur+r2.largeur);
return R;}
int main () {
//saisie des longueurs (L1, L2) et largeurs (l1, l2) de 2 rectangles rect1 et rect2
double L1, l1, L2, l2;
cout<<"Longueur rectangle rect1 : ";
cin>>L1;
cout<<"Largeur rectangle rect1 : ";
cin>>l1;
cout<<"Longueur rectangle rect2 : “;
cin>>L2;
cout<<"Largeur rectangle rect2 : ";
cin>>l2;
//instanciation de rext1 et rect2
rectangle rect1(L1, l1);
rectangle rect2(L2, l2);
//addition des objets rect1 et rect2 pour créer l’objet rect3
rectangle rect3=rect1+rect2;
//affichage des dimensions de rect3
rect3.affiche();
return 0;
}
26
HERITAGE
Objectifs du cours :
Ce cours a pour but :
Du point de vue de la programmation, la relation a-un est une inclusion entre classes. Ainsi, un objet
de type Voiture renferme une donnée-membre qui est un objet de type Moteur.
La relation utilise-un est une collaboration entre classes indépendantes. Elle se traduit le plus souvent
par des pointeurs. Par exemple, un objet de type Voiture renfermera une donnée-membre de type
pointeur sur Route, ce qui permettra à la voiture de communiquer avec une route.
La relation est-un s’appelle un héritage : une voiture hérite des caractéristiques communes à tout
véhicule.
On dira que la classe Voiture est dérivée de la classe Véhicule.
I. PRESENATION DE L’HERITAGE :
1. Définition de l’Héritage :
Après les notions d’encapsulation et d’abstraction, le troisième aspect essentiel des objets est la
notion d’héritage.
L’héritage est une technique extrêmement efficace pour créer des classes plus spécialisées, appelées
sous-classes, à partir de classes plus générales déjà existantes, appelées super-classes.
Elle représente la relation «est-un».
Exemple : Hiérarchie de classes
Figure Géométrique
hérite de
Partitionnement
Rectangle Cercle
Spécialisation
Sphère Cylindre
27
Plus précisément, lorsqu’une sous-classe C1 est créée à partir d’une classe C, C1 va hériter de
l’ensemble des attributs et des méthodes de C (sauf les constructeurs/destructeurs).
NB :
Les attributs et les méthodes de C vont être disponibles pour C1 sans que l’on ait besoin de
les redéfinir explicitement dans C1.
De plus :
Le type est aussi hérité : C1 est (aussi) un C. Pour un C x; et un C1 y;, on peut tout à
fait faire x = y; (mais bien sûr pas y = x; !!!)
Par transitivité, les instances d’une sous-classe possèdent les attributs et méthodes
de l’ensemble des classes parentes (classe parente, classe parente de la parente, etc.)
Par ailleurs :
Des attributs et/ou méthodes supplémentaires peuvent être définis par la sous-classe
C1. Ces membres constituent l’enrichissement apporté par cette sous-classe.
La notion d’enrichissement par héritage :
crée un réseau de dépendances entre classes,
ce réseau est organisé en une structure arborescente où chacun des nœuds
hérite des propriétés de l’ensemble des nœuds du chemin remontant jusqu’à la
racine. Ce réseau de dépendance définit une hiérarchie de classes
2. L’avantage de l’héritage :
d’expliciter des relations structurelles et sémantiques entre classes ;
de réduire les redondances de description et de stockage des propriétés.
du point de vue du développemente te de la maintenance, l’héritage permet de gérer le
partage, la réutilisabilition et l’extension du code
hérite de
Classe RectangleColore
couleur
On évite ainsi de dupliquer inutilement du code commun aux classes Rectangle et RectangleColore !
28
II. LES MEMBRES D’UNE SOUS-CLASSE
1. Accès aux membres d’une sous-classe :
Jusqu’à maintenant, l’accès aux membres (attributs et méthodes) d’une classe pouvait être :
soit public : visibilité totale à l’intérieur et à l’extérieur de la classe (mot-clé public)
soit privé : visibilité uniquement à l’intérieur de la classe (mot-clé private ou par défaut)
Un troisième type d’accès régit l’accès aux attributs/méthodes au sein d’une hiérarchie de classes :
L’accès protégé : assure la visibilité des membres d’une classe dans les classes de sa
descendance [et uniquement elles, et uniquement dans ce rôle (de sous classe, voir exemple
plus loin)]. Le mot clé est «protected».
3. Accès protégé :
Le niveau d’accès protégé correspond à une extension du niveau privé aux membres des sous-
classes
Exemple :
class Rectangle
{
public:
Rectangle(): largeur(1.0), hauteur(2.0) {}
protected:
double largeur; double hauteur;
};
class RectangleColore : public Rectangle
{
public:
// on profite ici de protected
void carre() { largeur = hauteur; }
protected:
Couleur couleur;
};
Le niveau d’accès protégé correspond à une extension du niveau privé aux membres des sous-
classes... mais uniquement dans ce rôle (de sous-classe) pas dans le rôle de classe extérieure :
29
Exemple :
class A {
//...
protected: int a;
private: int p;
};
class B: public A {
public:
//...
void f(B autreB, A autreA, int x) {
a = x; // OK A::a est protected => accès possible
p = x; // erreur : A::p est private
a += autreB.p; // erreur (même raison)
a += autreB.a; // OK : dans la même classe (B)
a += autreA.a; // INTERDIT ! : this n’est pas de la même classe que autreA (role externe)
}
};
Récapitulatif des changements de niveaux d’accès aux membres hérités, en fonction du niveau initial
et du type d’héritage :
accès initial
public protected private
public public protected pas d’accès héritage
héritage
30
III. MASQUAGE DANS UNE HIERARCHIE :
1. Présentation du masquage dans une hiérarchie :
Rectangle
…
Double surface() {
Return (largeur*hauteur) ;}
hérite de
Rectangle3D:Rectangle
…
double surface() {
return(2*largeur*hauteur +
2*largeur*…) ;}
class Rectangle {
public:
// les constructeurs seraient ici...
double surface() {return (largeur*hauteur);}}
protected:
double largeur; double hauteur;
// le reste de la classe...
};
class Rectangle3D : public Rectangle {
public:
// les constructeurs seraient ici...
double surface() { // Masquage !
return(2.0*(largeur*hauteur) + 2.0*(largeur*profondeur)+ 2.0*(hauteur*profondeur));
}
protected:
double profondeur;
// le reste de la classe...
};
31
2. Accès à une méthode masquée :
Il est parfois souhaitable d’accéder à une méthode/un attribut caché(e),
Exemple :
o surface des Rectangle3D ayant une profondeur nulle (largeur*hauteur). Identique au
calcul de surface pour les Rectangle.
Code désiré :
1. Objet non-Rectangle3D : Méthode générale (surface de Rectangle)
2. Objet Rectangle3D : Méthode spécialisée (surface de Rectangle3D)
3. Objet Rectangle3D de profondeur nulle :
o D’abord la méthode spécialisée
o Ensuite appel à la méthode générale depuis la méthode spécialisée
Pour accéder aux attributs/méthodes caché(e)s de la super-classe, on utilise l’opérateur de
résolution de portée :
o Syntaxe : NomClasse ::methode ou attribut
o Exemple : Rectangle ::surface()
32
Lorsque la super-classe admet un constructeur par défaut, l’invocation explicite de ce constructeur
dans la sous-classe n’est pas obligatoire. Par ce que le compilateur se charge de réaliser l’invocation
du constructeur par défaut.
Si la classe parente n’admet pas de constructeur par défaut, l’invocation explicite d’un de ses
constructeurs est obligatoire dans les constructeurs de la sous-classe. Ce qui implique que la sur-
classe doit admettre au moins un constructeur explicite.
Exemple :
class Rectangle {
protected: double largeur; double hauteur;
public:
Rectangle(double l, double h) : largeur(l), hauteur(h) {}
// le reste de la classe...
};
class Rectangle3D : public Rectangle {
protected: double profondeur;
public:
Rectangle3D(double l, double h, double p)
// Appel au constructeur de la super-classe
: Rectangle(l,h), profondeur(p) {}
// le reste de la classe...
};
Autre exemple (qui ne fait pas la même chose) :
class Rectangle {
protected: double largeur; double hauteur;
public:
// il y a un constructeur par defaut !
Rectangle() : largeur(0.0), hauteur(0.0)
{}}
// le reste de la classe...
};
class Rectangle3D : public Rectangle {
protected: double profondeur;
public:
Rectangle3D(double p)
: profondeur(p)
{}
// le reste de la classe...
};
Ici, il n’est pas nécessaire d’invoquer explicitement le constructeur de la classe parente puisque celle-
ci admet un constructeur par défaut.
Encore un exemple :
Il n’est pas nécessaire d’avoir des attributs supplémentaires...
class Carre : public Rectangle {
public:
Carre(double taille) : Rectangle(taille, taille){}
// et c’est tout ! (sauf s’il y avait des "méthodes set")
};
33
2. Ordre d’appel des constructeurs dans le cas de plusieurs héritages :
Hiérachie de classes constructeurs
Classe A A(…)
: a1(…),
a1 m1(…) a2(…)
a2 m2(…) {}
Classe B B(…)
: A(…),
b1 m3(…) B1(…)
m4(…) {}
Classe C C(…)
: B(…),
c1 m5(…) c1(…), c2(…)
c2 {}
Instanciation ;
C monC(…) ;
34
La solution à ce problème consiste à utiliser un attribut statique :
un attribut statique est partagé par toutes les instances de la même classe (on parle aussi
d’« attribut de classe »),
c’est un attribut de la classe qui peut être privé, protégé ou public et dont la déclaration est
précédée du mot clé static,
il existe même lorsqu’aucune instance de la classe n’est déclarée.
35
COMPLEMENTS SUR L’HERITAGE
Spécifier une classe dérivée
class Personne{
public :
Personne(char* n, char s, int a);
char* getNom();
char getSexe();
int getAge();
void setNom(char* n);
void setAge(int a);
~Personne() {}
private :
char nom[40];
char sexe;
int age, taille, poids;
};
class Employe : public Personne
{
public :
Employe(char* n, char s, int a, int n=0) :Personne(n, s, a) {salaire = n ;}
int getSalaire(){return=salaire ;}
void setSalire(int a){salaire=a ;}
~Employe() {}
private :
int salaire;
};
36
void setSalire(int a){salaire=a ;}
~Employe() {}
private :
int salaire;
};
Ajouter le polymorphisme
class Personne{
public :
Personne(char* n, char s, int a);
char* getNom();
char getSexe();
int getAge();
void setNom(char* n);
void setAge(int a);
~Personne() {}
private :
char nom[40];
char sexe;
int age, taille, poids;
};
class Employe : public Personne
37
{
public :
Employe(char* n, char s, int a, int n=0) :Personne(n, s, a) {salaire = n ;}
int getSalaire(){return=salaire ;}
virtual void setSalire(int a){salaire=a ;}
~Employe() {}
private :
int salaire;
};
Classes abtraites
La classe de base aussi appelée classe racine d’une hiérarchie peut contenir des fonctions virtuelles. Il
s’agit souvent de fonctions factices, dont le corps est vide, et qui ne prennent une signaturesécifique
qu’à l’interieur d’une classe dérivée. En C++, cette situation est gérée par le mécanisme virtuel pur.
38
ACTIVITES :
EXERCICE 1 :
1. Présenter la programmation orientée objet à travers ses caractéristiques et son intérêt en quelques points.
2. Donner la définition des concepts et expressions suivants dans la Programmation Orientée Objet
(POO) : La surcharge des opérateurs en C++, une fonction amie dans une classe, une classe abstraite,
le polymorphisme.
EXERCICE 2 :
1. On considère que la classe C hérite de la classe A et de la classe B. Une instance de la classe C
possède à la fois les données et fonctions membres de la classe A et celles de la classe B. VRAI ou
FAUX ? De quel principe s’agit-il ?
2. Quand un objet de la classe C est créé, les constructeurs des classes parentes sont-ils appelés ?
Si oui, dans quel ordre ? Si non, pourquoi ?
3. Quand un objet est détruit, les destructeurs des classes parentes sont-ils appelés ? Au cas où il
y a appel, cet appel se fait-il avant ou après celui de l’objet détruit ?
4. Dans la situation ci-dessus (1), il peut arriver que des données ou fonctions-membres des
classes A et B aient le même nom. Que fait-on pour lever l’ambigüité ? Donnez en un
exemple avec un commentaire en C++.
EXERCICE 3 :
1. Commentez le code en C++ ci-dessous, comme cela est indiqué par l’expression « commentaire ici ».
2. Recensez les concepts de la programmation orientée objet en C++ évoqués dans le programme ci-
dessous.
#include <iostream>
using namespace std;
//commentez ici
class polygone{
public :
void valorise(double a, double b){
l=a; h=b;
}
//commentez ici
virtual double surf(void){
cout<<"Appel de la fonction surf de la classe de
base qui renvoie : ";
return 0;
}
protected:
double l, h;
};
//commentez ici
class rectangle:public polygone
{
39
public:
double surf(void){
cout<<"Appel de la fonction surf de la classe de
base qui renvoie : ";
return l*h; }
};
//définition de la classe triangle
class triangle:public polygone{
public:
double surf(void){
cout<<"Appel de la fonction surf de la classe de
base qui renvoie : ";
return l*h/2;
}
};
int main(){
//commentez ici
rectangle R;
triangle T;
polygone P;
//commentez ici
polygone *ptP1=&R;
polygone *ptP2=&T;
polygone *ptP3=&P;
//commentez ici
ptP1->valorise(5, 3);
ptP2->valorise(2.5, 1.5);
ptP3->valorise(10, 6);
//commentez ici
cout<<ptP1->surf()<<endl;
//commentez ici
cout<<ptP2->surf()<<endl;
//commentez ici
cout<<ptP3->surf()<<endl;
return 0;
}
40
EXERCICE 4 :
1. Définir une classe Copain pour constituer un répertoire téléphonique avec les attributs
suivants : nom, prenom, tel.
2. Définir à l’aide des propriétés les méthodes d’accès aux différents attributs de la classe.
3. Définir un constructeur permettant d’initialiser les attributs de la méthode par des valeurs
saisies par l’utilisateur.
4. Définir la méthode Afficher( ) permettant d’afficher les informations du copain en cours.
5. Écrire un programme testant la classe Copain. Les valeurs des attributs des objets seront
renseignées par l’utilisateur de votre programme.
Attention : l’action de la modification du numéro de téléphone doit être soumise à la
réponse à la question suivante : « Voulez-vous vraiment modifier le numéro de
téléphone ? (O/N) »
EXERCICE 5 :
1. Définir une classe Employé caractérisée par les attributs : Matricule, Nom, Prénom, DateNaissance,
DateEmbauche, Salaire.
2. Définir à l’aide des propriétés les méthodes d’accès aux différents attributs de la classe.
3. Définir un constructeur permettant d’initialiser les attributs de la méthode par des valeurs saisies par
l’utilisateur.
4. Ajouter à la classe la méthode Age( ) qui retourne l’âge de l’employé.
5. Ajouter à la classe la méthode Anciennete( ) qui retourne le nombre d’années d’ancienneté de
l’employé.
6. Ajouter à la classe la méthode AugmentationDuSalaire( ) qui augmente le salaire de l’employé en
prenant en considération l’ancienneté.
NB : Si Ancienneté < 5 ans, alors on ajoute 2%. - Si Ancienneté < 10 ans, alors on ajoute 5%. - Sinon, on
ajoute 10%.
7. Ajouter la méthode AfficherEmployé() qui affiche les informations de l’employé comme suit :
- Matricule : […]
- Age : […]
- Ancienneté : […]
- Salaire : […]
41
Ecrire un programme de test pour la classe Employé.
42