Chapitre 1
Les Classes et les objets
La programmation procédurale est une méthode d'écriture de logiciel centrée sur les
procédures ou les actions qui se déroulent dans un programme.
La programmation orientée objet est centrée sur les objets. Ces objets sont créés à partir de
types de données abstraits qui encapsulent les données et les fonctions ensembles.
class nomDeLaClasse{
};
Important :
Le point-virgule à la fin de la déclaration d’une classe est très très très très important.
Exemple : le code suivant déclare une classe nommée Rectangle avec deux variables membres
: largeur et Longueur.
Il y a cependant un problème avec cette classe. Contrairement aux structures, les membres d'une
classe sont privés par défaut. Les membres de classe privés ne sont pas accessibles par des
instructions de programmation en dehors de la classe, ils sont protégés du code extérieur. Ainsi,
aucune instruction en dehors de cette classe Rectangle ne peut accéder aux membres Longueur
et Largeur.
a- Spécificateurs d'accès
C ++ fournit des mots clés :
• Private ;
• Public ;
• Protected ;
appelés spécificateurs d'accès que l’on utilise dans les déclarations de classe et qui permettent
de spécifier comment les membres de la classe peuvent être accessibles.
Voici le format général d'une déclaration de classe qui utilise les spécificateurs d'accès privé,
public et protégé.
class nomDeLaClasse{
private :
... // placer ici les membres privés
public :
... // placer ici les membres publics
protected :
... // placer ici les membres privés
};
Important :
Notez que les spécificateurs d'accès sont suivis de deux points (:), puis d'une ou plusieurs
déclarations de membre. Dans ce format général, le spécificateur d'accès private est utilisé
en premier. Toutes les déclarations qui le suivent, jusqu'au spécificateur d'accès public,
sont destinées aux membres privés. Ensuite, toutes les déclarations qui suivent le
spécificateur d'accès public sont destinées aux membres publics. Toutes les déclarations
qui le suivent, jusqu'au spécificateur d'accès protected, sont destinées aux membres public.
Enfin, toutes les déclarations qui suivent le spécificateur d'accès protected sont destinées
aux membres protégés.
Fonctions
Description
membres
SetLargeur Cette fonction accepte un argument, qui permet de modifier la variable membre Largeur
setLongueur Cette fonction accepte un argument, qui permet de modifier la variable membre Longueur
getLargeur Cette fonction renvoie la valeur stockée dans la variable membre Largeur.
getLongueur Cette fonction renvoie la valeur stockée dans la variable membre Longueur.
Cette valeur correspond à l'aire du rectangle. Cette fonction renvoie le produit des
getSurface
variables membres Largeur fois Longueur.
getPerimetre Cette valeur correspond au Perimètre du rectangle.
Pour le moment, nous ne définirons pas réellement les fonctions décrites. Nous ne ferons que
les déclarations des prototypes des fonctions dans la déclaration de la classe Rectangle :
Dans cette déclaration, la largeur et la longueur des variables membres sont déclarées comme
privées, ce qui signifie qu’elles ne sont accessibles que par les fonctions membres de la classe.
Les fonctions membres, cependant, sont déclarées comme publiques, ce qui signifie qu'elles
peuvent être appelées à partir d'instructions externes à la classe. Si le code en dehors de la classe
doit stocker une largeur ou une longueur dans un objet Rectangle, il doit le faire en appelant les
fonctions membres setLargeur ou setLongueur de l'objet. De même, si du code externe à la
classe doit récupérer une largeur ou une longueur stockée dans un objet Rectangle, il doit le
faire avec les fonctions membres getLargeur ou getLongueur de l'objet. Ces fonctions
publiques fournissent une interface pour le code en dehors de la classe pour utiliser des objets
Rectangle.
Important :
Même si l'accès par défaut à une classe est privé, il est toujours judicieux d'utiliser le
mot clé privé pour déclarer explicitement les membres privés. Cela documente
clairement la spécification d'accès de tous les membres de la classe.
Lorsque le mot clé const apparaît après les parenthèses dans une déclaration d’une fonction
membre, il spécifie que la fonction ne modifiera aucune donnée stockée dans l'objet appelant.
Si vous écrivez par inadvertance du code dans la fonction qui modifie les données de l'objet
appelant, le compilateur générera une erreur.
Important :
Lorsque vous marquez une fonction membre comme const, le mot clé const doit
apparaître à la fois dans l'en-tête et dans la déclaration de la fonction. Étant donné que
cela diminue les chances d'avoir des bogues dans votre code, il est recommandé de
marquer toutes les fonctions d'accesseur comme const.
typeDeRetourDeLaFonction nomDeLaClasse::NomDeLaFonction(ListePArametres)
La déclaration de la classe Rectangle contient les déclarations ou les prototypes des fonctions
membres.
Les définitions de ces fonctions sont écrites en dehors de la déclaration de la classe :
e- Accesseurs et mutateurs
Comme mentionné précédemment, il est courant de rendre privées toutes les variables membres
d'une classe et de fournir des fonctions membres publiques pour y accéder et les modifier. Cela
garantit que l'objet propriétaire des variables membres contrôle toutes les modifications qui y
sont apportées.
Une fonction membre qui obtient une valeur de la variable membre d'une classe mais ne la
modifie pas est appelée accesseur.
Une fonction membre qui stocke une valeur dans une variable membre ou modifie la valeur
d'une variable membre est appelée mutateur.
Dans la classe Rectangle, les fonctions membres getLongueur et getLargeur sont des
accesseurs et les fonctions membres setLongueur et setLargeur sont des mutateurs.
Certains programmeurs se réfèrent aux mutateurs comme des fonctions de définition parce
qu'ils définissent la valeur d'un attribut et aux accesseurs comme des fonctions de lecture parce
qu'ils obtiennent la valeur d'un attribut.
nomDeLaClasse nomDeLObjet ;
Exemple :
Rectangle R1;
La définition d'un objet de classe s'appelle l'instanciation d'une classe. Dans cette instruction,
R1 est une instance de la classe Rectangle.
L'objet R1 que nous avons précédemment défini est une instance de la classe Rectangle.
Supposons que nous voulions changer la valeur de la variable Largeur de l'objet R1. Pour ce
faire, nous devons utiliser l'objet R1 pour appeler la fonction membre setLargeur, comme
indiqué ci-dessous :
R1.setLargeur(9.15);
Tout comme vous utilisez l'opérateur point pour accéder aux membres d'une structure, vous
utilisez l'opérateur point pour appeler les fonctions membres d'une classe. Cette instruction
utilise l'objet R1 pour appeler la fonction membre setLargeur, en passant 9.15 comme
argument. Par conséquent, la variable de largeur de l’objet R1 sera définie sur 9,15.
~TP : Ecrivez un programme demandant à un user la longueur et la largeur du rectangle,
ensuite les affiche, ainsi que son périmètre et sa surface~
Le pointeur ptrRect peut ensuite être utilisé pour appeler des fonctions membres à l'aide de
l'opérateur ->. Les déclarations suivantes montrent des exemples :
Nous voyons que l’instruction de la ligne 11 appelle la fonction membre setLargeur, en passant
9.15 comme argument. Étant donné que ptrRect pointe vers l’objet monRectangle, cela
entraînera le stockage de 9.15 dans la variable de largeur de l’objet monRectangle. De même à
la ligne 12 l’instruction appelle la fonction membre setLongueur, en transmettant 2.4 comme
argument. Cela entraînera le stockage de 2.4 dans la variable de longueur de l'objet
monRectangle.
Les pointeurs d'objet de classe peuvent être utilisés pour allouer dynamiquement des objets.
Nous pouvons reécrire le code ci-dessus comme suit :
La ligne 10 définit ptrRect comme un pointeur de type Rectangle. La ligne 13 utilise l'opérateur
new pour allouer dynamiquement un objet Rectangle et affecter son adresse à ptrRect. Les
lignes 16 et 17 stockent les valeurs dans les variables Largeur et Longueur de l’objet alloué
dynamiquement.
La ligne 12 supprime l'objet de la mémoire et la ligne 13 replace le pointeur à l'adresse null,
cela empêche le code d'utiliser par inadvertance le pointeur pour accéder à la zone mémoire qui
a été libérée. Il empêche également les erreurs de se produire si la suppression du nouveau le
pointeur est accidentellement appelée.
~TP~
Une fois que vous avez défini un unique_ptr, vous pouvez l'utiliser de la même manière qu'un
pointeur normal.
À la ligne 3, nous avons une directive #include pour le fichier d'en-tête de la mémoire. La ligne
9 définit un unique_ptr, nommé ptrRect, qui pointe vers un rectangle alloué dynamiquement.
Notez qu'il n'y a pas d'instructions de suppression à la fin de la fonction main pour libérer la
mémoire allouée dynamiquement. Il n'est pas nécessaire de supprimer l’objet Rectangle alloué
dynamiquement car le pointeur intelligent les supprimera automatiquement à la fin de la
fonction.
Dans la programmation orientée objet, un objet doit protéger ses données importantes en les
rendant privées et en fournissant une interface publique pour accéder à ces données. Vous
pouvez vous demander pourquoi les fonctions membres ont été définies pour des tâches aussi
simples que la modification et l’acquisition des variables membres. Après tout, si les variables
membres étaient déclarées comme publiques, les fonctions membres ne seraient pas
nécessaires.
Comme mentionné précédemment dans ce chapitre, les classes ont généralement des variables
et des fonctions destinées uniquement à être utilisées en interne. Ils ne sont pas destinés à être
accessibles par des instructions en dehors de la classe. Cela protège les données critiques contre
toute modification accidentelle ou utilisation d'une manière qui pourrait nuire à l'état de l'objet.
Lorsqu'une variable membre est déclarée comme privée, le seul moyen pour une application de
stocker des valeurs dans la variable est d’utiliser une fonction membre publique. De même, le
seul moyen pour une application de récupérer le contenu d'une variable membre privée est
d’utiliser une fonction membre publique. En substance, les membres publics deviennent une
interface avec l'objet. Ce sont les seuls membres auxquels toute application utilisant l'objet peut
accéder.
V- Les Constructeurs
Un constructeur est une fonction membre qui est automatiquement appelée lorsqu'un objet de
classe est créé. Il porte le même nom que la classe et est automatiquement appelé lorsque l'objet
est créé en mémoire ou instancié. Il est utile de considérer les constructeurs comme des routines
d'initialisation. Ils sont très utiles pour initialiser les variables membres ou effectuer d'autres
opérations de configuration.
Notez que l’en-tête de la fonction du constructeur est différent de celui d’une fonction membre
régulière. Il n'a pas de type de retour, même pas void. Cela est dû au fait que les constructeurs
ne sont pas exécutés par des appels de fonction explicites et ne peuvent pas renvoyer de valeur.
L’en-tête de fonction d’une définition externe d’un constructeur a la forme suivante :
nomDeLaClasse::nomDeLaClasse(listeParametres)
Dans le format général, nomDeLaClasse est le nom de la classe et listeParametres est une liste
facultative de déclarations de la liste de paramètres.
Nous tenons à préciser par l’exemple ci-dessous qu’un constructeur lorsqu’il est déclaré et
défini, est automatiquement appelé lors de l’instanciation de la classe.
Un constructeur peut avoir des paramètres et peut accepter des arguments lorsqu'un objet est
créé.
Les constructeurs peuvent accepter des arguments de la même manière que les autres fonctions.
Lorsqu'une classe a un constructeur qui accepte des arguments, vous pouvez transmettre des
valeurs d'initialisation au constructeur lorsque vous créez un objet.
Par exemple, le code suivant montre encore une autre version de la classe Rectangle. Cette
version a un constructeur qui accepte des arguments pour la largeur et la longueur du rectangle.
Fichier Rectangle.h
Fichier Rectangle.cpp
Fichier main.cpp :
VII- Destructeurs
Un destructeur est une fonction membre qui est automatiquement appelée lorsqu'un objet est
détruit.
Les destructeurs sont des fonctions membres portant le même nom que la classe, précédées d'un
caractère tilde (~). Par exemple, le destructeur de la classe Rectangle serait nommé ~Rectangle.
Les destructeurs sont automatiquement appelés lorsqu'un objet est détruit. De la même manière
que les constructeurs configurent les objets lors de la création d'un objet, les destructeurs
exécutent des procédures d'arrêt lorsque l'objet cesse d'exister. Par exemple, une utilisation
courante des destructeurs consiste à libérer de la mémoire allouée dynamiquement par l'objet
de classe.
Important :
En plus du fait que les destructeurs sont automatiquement appelés lorsqu'un objet est
détruit, les points suivants doivent être mentionnés :
• Comme les constructeurs, les destructeurs n'ont pas de type de retour.
a- Destructeurs et objets de classe alloués dynamiquement
• Les destructeurs ne peuvent pas accepter d'arguments, ils n'ont donc jamais de
liste de paramètres.
Si un objet de classe a été alloué dynamiquement par l'opérateur new, sa mémoire doit être
libéré lorsque l'objet n'est plus nécessaire.
Important :
Si vous avez utilisé un pointeur intelligent tel que unique_ptr (introduit dans C++11)
pour allouer un objet, l'objet sera automatiquement supprimé et son destructeur sera
appelé lorsque le pointeur intelligent sortira de la portée. Il n'est pas nécessaire
d'utiliser delete avec un unique_ptr.
Cette instruction définit un tableau de 12 objets de type Rectangle. Le nom du tableau est tR et
le constructeur par défaut est appelé pour chaque objet du tableau.
Si vous souhaitez définir un tableau d'objets et appeler un constructeur nécessitant des
arguments, vous devez spécifier les arguments de chaque objet individuellement dans une liste
d'initialiseurs. Voici un exemple :
ATTENTION :
Si la classe n'a pas de constructeur par défaut, vous devez fournir un initialiseur pour chaque
objet du tableau.
Si vous ne fournissez pas d'initialiseur pour tous les objets d'un tableau, le constructeur par
défaut sera appelé pour chaque objet qui n'a pas d'initialiseur. Par exemple, l'instruction suivante
définit un tableau de trois objets, mais ne fournit des initialiseurs que pour les deux premiers.
Le constructeur par défaut est appelé pour le troisième objet.
En résumé, si vous utilisez une liste d'initialiseurs pour les tableaux d'objets de classe, il y a
trois choses à retenir :
• S'il n'y a pas de constructeur par défaut, vous devez fournir un initialiseur pour chaque
objet du tableau.
• S'il y a moins d'initialiseurs dans la liste que d'objets dans le tableau, le constructeur par
défaut sera appelé pour tous les objets restants.
• Si un constructeur requiert plus d'un argument, l'initialiseur prend la forme d'un appel
de fonction constructeur.