1 Principes
Objet : propriétés + méthodes
Encapsulation : les actions sur les propriétés se font au travers des méthodes
Message : on dit que l’on envoie un message à un objet lorsque l’on utilise l’une de ses méthodes.
Classe : correspond au type dans les langages classiques. Les objets sont des instances d’une classe
Héritage : possibilité de définir une classe à partir d’une ou plusieurs autres classes
Polymorphisme : possibilité pour un objet d’une classe héritée de prendre la place d’un objet d’une
classe mère
Modularité : on peut réutiliser des classes. Chaque classe peut être compilée et testée séparément
Sécurité : on peut mettre à disposition des classes en prenant soin de protéger les données sensibles
Extensibilité : grâce à la possibilité d’utiliser des objets de classes prédéfinies et de surdéfinir les
opérateurs, on étend le langage à la manipulation d’objets nouveaux.
2.1 Déclarations
Elles peuvent se faire à tout instant. Ceci est indispensable en C++ dans la mesure où un objet est crée
lors de sa déclaration et qu’une méthode particulière appelée constructeur est automatiquement lancée lors de la
création d’un objet.
2.2 Surdéfinition
On peut définir plusieurs fois la même fonction si on peut les différencier par les paramètres (type et/ou nombre).
void sosie (int);
void sosie (float);
int n=5 ; float f;
sosie (n); // appelle la première fct
sosie (f); // appelle la seconde fct
2.3 Commentaires
Ils sont introduits par // et se terminent avec la ligne. /* et */ fonctionnent comme en C.
3 Classes
3.1 Déclaration
Entête de la classe placée en général dans un fichier .h ou .hpp qui sera inclus dans tout fichier utilisant cette
classe :
3.2 Utilisation
On va le réaliser avec un tableau de caractères (limité à 255 caractères) et le doter des méthodes suivantes :
affiche qui va afficher la chaîne de caractères
longueur qui retournera la longueur de la chaîne
car qui permettra de retrouver un caractère de rang donné dans la chaîne
majusc qui met en majuscule la première lettre de la chaîne
init_chaine qui place un contenu dans la chaîne
FICHIER CHAINE.HPP
class Chaine // chaîne de caractères avec tableau
{
protected:
char mot[255]; // tableau de caractères accessible dans les classes dérivées
private:
int lg; // longueur de la chaîne inaccessible
public:
void init_chaine(char *); // initialisation du contenu
void affiche(); // affichage de la chaîne
int longueur(); // retourne la longueur de la chaîne
char car(int); // retourne le caractère placé dans la position donnée en paramètre
void majusc(); // met la première lettre de la chaîne en majuscule
};
FICHIER CHAINE.CPP
#include <iostream> // pour utiliser cout
#include <string.h> // pour utiliser strlen ...
#include <ctype.h> // pour utiliser toupper
void Chaine::affiche() {
std::cout<<mot; // affichage de la chaîne
}
int Chaine::longueur() {
return lg; // c’est tout
}
char Chaine::car(int rang) { // retourne le caractère placé dans la position donnée en paramètre ou le dernier
if (rang < lg) return mot[rang];
else return mot[lg-1];
}
en C en C++
Toute fonction doit être déclarée en C++. Cette déclaration se fait par un prototype de la forme :
int f (int , char);
Pour une méthode la syntaxe est la même en préfixant le nom de la fonction par celui de la classe à laquelle elle
appartient.
3.3.1 Référence
3.3.1.1 Le passage de paramètre par référence se fait par :
void echange (int & , int &); // déclaration de prototype
1°) Notation : Elle est notée par un nom de type ou de classe suivi du symbole &.
ATTENTION il ne faut pas confondre ce symbole & avec celui utilisé pour obtenir une adresse ainsi :
int & x veut dire que x est une référence à un entier
tandis que a = &x veut dire que a reçoit l’adresse de x
2°) Utilisation : En paramètre pour que la fonction ou la méthode puisse accéder au paramètre (passage par
adresse dans d’autres langages)
3.3.1.2 En retour : la fonction ou la méthode retourne une référence à une variable. Cette fonction ou méthode
pourra donc prendre place partout où l’on pourrait mettre une variable. On pourra avec une fonction f définie par
: int & f(char) écrire f(‘q’) = 0 qui mettra donc à 0 la variable à laquelle f fait référence en retour.
ATTENTION : on ne peut faire référence qu’à quelque chose qui existe. Ainsi une fonction g écrite comme suit :
int & g(int x) {
int z;
z=x+1;
return z;
}
n’a aucun sens puisque la valeur retournée par g est une référence à z qui est locale à g et n’existe donc plus dès
que g est terminée !
4.1 Constructeur
C’est une méthode appelée lors de la création de l’objet. Elle porte le même nom que la classe.
Point (int, int); // prototype du constructeur
Dans ce cas, lorsque les objets sont crées, le constructeur est appelé. Il faut faire : Point a(1,3);
ATTENTION : le constructeur doit être public. Il sera appelé à chaque création d’objet sauf si cette création est
faite à partir d’un autre objet par : Point a=b; dans ce cas il faudra prévoir un constructeur spécial (constructeur
par recopie).
Appel : Le constructeur sera appelé lors de chaque création d’un objet nouveau c’est à dire lors d’une déclaration
ou d’une utilisation de new sur un pointeur vers un objet.
FICHIER CHAINE.HPP
FICHIER CHAINE.CPP
4.2 Destructeur
C’est une méthode appelée lors de la destruction de l’objet. Elle porte le même nom que la classe,
précédé du symbole ~
~Point(); // prototype du destructeur
Le destructeur est en général utilisé si certaines actions du constructeur doivent être annulées (affichages,
ouverture de fichiers, allocation dynamique de mémoire, etc.).
Il sera appelé à chaque destruction d’objet même si le constructeur ne l’a pas été.
Modification de la réalisation en utilisant un pointeur sur des caractères au lieu d’un tableau.
Il va falloir faire de l’allocation dynamique de mémoire dans le constructeur et ajouter un destructeur pour libérer
cette mémoire
FICHIER CHAINE.HPP
class Chaine // chaîne de caractères avec pointeur et allocation dynamique de mémoire
{
protected:
char *ptr; // pointeur vers la chaîne
private:
int lg; // longueur de la chaîne
public:
Chaine (char *); // constructeur avec initialisation du contenu et réservation de mémoire
~chaine(); // destructeur pour libérer la place allouée
void init_chaine(char *); // modification du contenu dans la chaîne
void affiche(); // affichage de la chaîne
int longueur(); // retourne la longueur de la chaîne
char car(int); // retourne le caractère placé dans la position donnée en paramètre
void majusc(); // met la première lettre de la chaîne en majuscule
};
FICHIER CHAINE.CPP
#include <iostream> // pour utiliser cout
#include <string.h> // pour utiliser strlen ...
#include <ctype.h> // pour utiliser toupper
Chaine::~Chaine () { // destructeur
delete [] ptr; // libération de la mémoire
// remarque : avec certains compilateurs C++ il faudra écrire : delete [lg+1] ptr;
}
int Chaine::longueur() {
return lg; // c’est tout
}
char Chaine::car(int rang) { // retourne le caractère placé dans la position donnée en paramètre ou le dernier
if (rang < lg) return *(ptr + rang);
else return *(ptr + lg - 1);
}
5 Surdéfinition de méthodes
On peut donner le même nom à plusieurs méthodes à condition qu’elles diffèrent par les paramètres :
Point::Point (); // constructeur sans paramètre
Point::Point (int,int); // constructeur à 2 paramètres
Chaine::Chaine (int longueur, char c) { // constructeur avec initialisation par un caractère répété
lg=longueur;
ptr = new char[lg+1];
for (int i=0; i < lg; i++) { *(ptr+i) = c; }
*(ptr+lg) = ‘\0’; // marqueur de fin
}
Ainsi Chaine ch1(‘’bonjour ’’); // crée une chaîne de 7 caractères contenant le mot ‘bonjour’
Chaine ch2(22,’*’); // crée une chaîne de 22 caractères *
Chaine ch3; // crée une chaîne vide
L’existence d’un tel constructeur évitera le problème soulevé par : Expl b=a; qui fait une copie de a dans le
nouvel objet b sans tenir compte des zones de mémoire pointées dans a (on ne recopie que les pointeurs et non
les zones pointées). Il faudra écrire ce constructeur de façon a ce qu’il prenne en charge la recopie correcte de a
dans b.
Appel : Le constructeur par recopie sera automatiquement appelé lorsqu’un objet nouveau sera déclaré avec
comme valeur initiale celle d’un autre objet ( Point b=a; ). Il le sera également lors de tout passage de paramètre
par valeur pour créer la copie du paramètre. Il sera également utilisé lors d'un retour par valeur pour créer l'objet
retourné.
REMARQUE : Un constructeur par recopie est nécessaire pour tout objet utilisant de l’allocation dynamique de
mémoire dès lors que l’on désire pouvoir passer un tel objet en paramètre par valeur à des fonctions. En effet, un
passage par valeur consiste en la création d’un nouvel objet par recopie du paramètre. En l’absence de
constructeur approprié la recopie de fait simplement par copie des membres y compris des pointeurs.
Addition d ’un constructeur par recopie permettant de passer les chaînes en paramètre par valeur à des fonctions
10 Fonction amie
Il s’agit d’une méthode d’une classe ou d’une fonction indépendante pouvant accéder à des membres d’une autre
classe ou de plusieurs. Par exemple pour créer des opérations entre objets de classes différentes (produit de
vecteur par matrice).
On la déclare dans la classe à laquelle elle doit pouvoir accéder par : friend int fct(int, Expl); s’il s’agit d’une
fonction indépendante ou par : friend int Point::fct(int, Expl); s’il s’agit d’une méthode (ici de Point). Dans ce
cas il faut que la classe Point soit compilée en premier.
Si toutes les méthodes d’une classe A sont amies d’une autre classe B on pourra déclarer dans B : friend class A;
Création d’une fonction permettant de remplacer dans une chaîne un caractère par le caractère de même rang pris
dans pris dans une autre chaîne
Il faut pouvoir modifier les caractères de la chaîne ce qui n’est pas possible car le membre ‘ptr’ est privé. Deux
solutions sont possibles :
On crée une méthode de la classe Chaine
On crée une fonction amie de la classe Chaine
Si l’on choisit la première solution on utilisera cette méthode par : machaine.modifie(rang , modele)
Tandis que si l’on choisit la seconde on écrira : modifie (machaine, rang, modele)
Pour traiter cet exemple nous choisirons de définir une fonction amie.
ATTENTION : La fonction modifie reçoit un paramètre de classe Chaine par valeur (le dernier) cela peut
fonctionner parce qu’on a défini un constructeur par recopie à la classe Chaine. En l’absence de constructeur par
recopie le fonctionnement aurait été le suivant:
Lors de l’appel de la fonction, une copie du paramètre est faite. Cette copie possède un membre ptr égal
à celui de l’original
A la fin de la fonction cette copie est détruite par le destructeur que nous avons défini par conséquent la
mémoire pointée par le membre ptr de la copie est libérée. Or cette mémoire est aussi celle de l’original qui se
voit ainsi privé de sa zone de mémoire !
On aurait pu, bien entendu, contourner ce problème en passant toujours les paramètres de classe Chaine par
référence et l’entête de la fonction amie deviendrait : friend int modifie(Chaine &, int, Chaine &);
C’est ce que l’on avait fait lors de l’écriture de la méthode ajoute à laquelle on avait passé un paramètre de classe
Chaine par référence alors qu’il n’était pas modifié par la méthode.
11 Surdéfinition d’opérateur
On peut surdéfinir les opérateurs (+ - = < etc.).
11.1 Surdéfinition
1°) Avec une fonction externe
Si l’on veut surdéfinir + pour une classe Complexe on fera :
friend Complexe operator + (Complexe &,Complexe &); // dans la classe complexe
et Complexe operator + (Complexe & a, Complexe & b)
{ corps de l’opérateur } // fonction indépendante
c=a+b; sera interprété par C++ comme c=operator + (a,b);
REMARQUE : la surdéfinition de = permettra d’éviter les problèmes d’affection d’objets contenant une partie
dynamique. Toutefois l’existence d’un tel opérateur n’évite pas le constructeur par recopie lorsque celui ci fait
une allocation dynamique de mémoire (qui ne doit être, en général, faite que lors de la création de l’objet et non
à chaque affectation)
L’affectation b=a n’a pas recopié le contenu de a dans b mais seulement le pointeur et toute modification sur a
(majusc) est visible sur b (dernier affichage de b).
La classe Nouvelle hérite de la classe Ancienne. Si l’on a utilisé le terme public la partie publique de Ancienne
l’est aussi dans Nouvelle alors que si l’on a utilisé private la partie publique de Ancienne est accessible par le
concepteur de Nouvelle mais pas par les utilisateurs.
La conception de Nouvelle peut utiliser tout ce qui est public et protected dans Ancienne. Elle n’autorise pas les
accès aux membres ou méthodes privées de Ancienne.
L’utilisateur d’un objet de la classe Nouvelle pourra utiliser les méthodes publiques de Ancienne et celles de
Nouvelle.
Si des méthodes de Nouvelle ont le même nom que certaines existant dans Ancienne, elles viennent les cacher
(surcharge).
Création d’une nouvelle classe : Chaine-coloree permettant d’avoir une chaîne de caractères à laquelle sont
associées deux couleurs (fond et texte).
Nous allons doter cette nouvelle classe d’une méthode permettant de choisir les deux couleurs et d’un
constructeur permettant d’initialiser une chaîne et ses couleurs.
Il faudra aussi surcharger la méthode d’affichage pour tenir compte des couleurs.
FICHIER CHAINECL.HPP
#include ‘’Chaine.hpp’’
void Chaine_coloree::affiche()
{
std::cout<<‘’<couleur_fond = ‘’;
std::cout<< couleur_fond.rouge()<<","<<couleur_fond.vert()<<","<<couleur_fond.bleu()
std::cout <<" ; couleur_texte = ";
std::cout<< couleur_texte.rouge()<<","<<couleur_texte.vert()<<","<<couleur_texte.bleu()
std::cout<<‘’>‘’; // indiquer les couleurs
Chaine::affiche(); // afficher la chaîne
}
Lorsque l’on crée une classe dérivée, on doit inclure dans son fichier d’en-tête celui de la classe mère. Ceci peut
poser le problème suivant :
Classe1 et Classe2 sont deux classes dérivées de la classe ClasseMere. Le fichier d’en-tête de Classe1, appelé
Classe1.hpp contient la ligne #include ‘’ClasseMere.hpp’’. De même le fichier d’en-tête de Classe2, appelé
Cclasse2.hpp contient la ligne #include ‘’ClasseMere.hpp’’. Si un programme doit utiliser des objets de la classe
Classe1 et des objets de la classe Classe2, il devra contenir les lignes :
#include ‘’Classe1.hpp’’
et #include ‘’Classe2.hpp’’
Lors de la compilation, on aura donc deux fois l’inclusion de ClasseMere.hpp (une fois à cause de Classe1.hpp et
l’autre à cause de Classe2.hpp) ce qui causera des erreurs de double définition. Pour éviter ceci, on utilisera dans
le fichier ClasseMere.hpp, une directive de compilation conditionnelle de la façon suivante :
#ifndef CLASSEMERE
#define CLASSEMERE
mettre ici le contenu du fichier ClasseMere.hpp tel qu’il était jusque là
Héritage multiple
Nous allons définir une nouvelle classe : style qui permettra de gérer le style du texte (police et aspect)
FICHIER STYLE.HPP
class Style
{
private :
char police; // une lettre pour définir la police
int aspect; // un code pour chaque combinaison des attributs (gras, souligné ...)
public:
Style(char, int); // constructeur avec initialisations
void choix_style(char, int); // modification du style
void affiche(); // affichage du style seul
};
FICHIER STYLE.CPP
#include <iostream>
#include ‘’Style.hpp ’’
Style::Style(char p, int a)
{
choix_style(p,a);
}
void Style::affiche()
{
std::cout<<‘’[‘’<<police<<‘’ , ‘’<<aspect<<‘’] ’’;
}
FICHIER CHAINEST.HPP
#include ‘’Chaine_coloree.hpp’’
#include ‘’Style.hpp’’
class Chaine_style : public Chaine_coloree, public style // construction par héritage multiple
{
public:
Chaine_style(char *, Couleur, Couleur,char , int); // constructeur avec : chaîne
// initiale, couleurs du fond et du texte, police et aspect
void affiche(); // surcharge de l’affichage
};
FICHIER CHAINEST.CPP
#include ‘’Chainest.hpp’’
Chaine_style::Chaine_style (char *chaineini, Couleur fond, Couleur texte, char police, int aspect):
Chaine_coloree(chaineini,fond,texte), Style(police,aspect)
{
// on n’a rien a ajouter dans ce constructeur puisqu’il fait appel à ceux de Chaine_coloree et de Style en leur
// transmettant les paramètres
}
void Chaine_style_affiche()
{
Style::affiche(); Chaine_coloree::affiche(): // affichage du style puis de la chaîne avec ses couleurs
}
12.5 Polymorphisme
Un objet d’une classe dérivée est objet de la classe de base et pas le contraire
Un objet de classe B dérivée de A peut être utilisé partout où un objet de A peut l’être.
avec A *ptra, obja;
B *ptrb, objb;
En général c’est la méthode de A qui sera prise car le compilateur ne sait pas au moment de la compilation vers
quoi pointe ptra (il suppose naturellement que c’est vert un objet de classe A puisque ptra a été déclaré comme
ça). Mais on peut modifier ce comportement en déclarant la méthode virtual dans la classe A. Ceci indique au
compilateur que cette méthode peut être redéfinie dans les classes dérivées et qu’il faudra décider au moment de
l’exécution laquelle il faut choisir selon l’objet réellement pointé.
Tout cela ne peut marcher que pour des objets dynamiques. En effet si l’on fait obja = objb; les méthodes
utilisables sont celles de A.
Les méthodes virtuelles sont définies par le prototype :
virtual void Expl::fct(int a, int b);
Problème du polymorphisme
- On a une erreur de compilation sur la ligne : ptr1 -> choix_couleurs(cl2,cl1); puisque l’objet désigné est de
classe Chaine et ne possède donc pas de méthode choix_couleurs.
- Si on supprime cette ligne et que l’on exécute le programme de test on obtient le résultat suivant :
chaine 1 C’est le résultat de ptr1 -> affiche()
<couleur_fond = 30000,5000,0 ; couleur_texte = 60000,25000,9990> chaine 2
C’est le résultat de ptr2 -> affiche()
chaine 2 C’est le résultat de ptr1 -> affiche()
En effet, le dernier appel de affiche() fait référence à la méthode définie dans Chaine et non dans Chaine_coloree.
Pour éviter ce problème il suffit de modifier le fichier CHAINE.HPP comme suit :
virtual void affiche(); // affichage de la chaîne pouvant être redéfini et choisi dynamiquement
14 Fonction statique
C’est une fonction indépendante de l’objet mais liée seulement à la classe. C’est en général une fonction
qui agit sur des membres communs.
static void Expl::fct()
On l’appellera sans désigner d’objet par Expl::fct();
11 SURDEFINITION D’OPERATEUR................................................................................................................................10
11.1 SURDEFINITION ...................................................................................................................................................................10
11.2 VALEUR EN RETOUR POUR UN OPERATEUR SURDEFINI ..........................................................................................................11
12 L’HERITAGE ....................................................................................................................................................................12
12.1 DEFINITION D’UNE CLASSE DERIVEE.....................................................................................................................................12
12.2 CONSTRUCTEUR, DESTRUCTEUR ..........................................................................................................................................12
13.3 REMARQUE CONCERNANT LE PROBLEME DES INCLUSIONS MULTIPLES D’UN FICHIER : ............................................................13
12.4 HERITAGE MULTIPLE ...........................................................................................................................................................14
12.5 POLYMORPHISME ................................................................................................................................................................15
12.6 CLASSE ABSTRAITE..............................................................................................................................................................16
13 MEMBRES COMMUNS...................................................................................................................................................17
14 FONCTION STATIQUE...................................................................................................................................................17
18 ENTREES / SORTIES.......................................................................................................................................................17
18.1 CLAVIER / ECRAN ..................................................................................................................................................................1
18.2 FICHIERS ................................................................................................................................ERREUR ! SIGNET NON DEFINI.