Encapsulation et modificateurs d’accès en C++ (public, protected, private)
Apprenez l’encapsulation et les modificateurs d’accès en C++ pour concevoir des classes sûres et maintenables.
Encapsulation (Encapsulation) est l’un des quatre principes fondamentaux de la programmation orientée objet (POO). Elle consiste à cacher les données (attributs) d’une classe au monde extérieur et à y accéder uniquement par des méthodes définies. Cette approche garantit l’intégrité des données et rend la structure interne d’une classe indépendante de son utilisation externe.
1) Qu’est-ce que l’encapsulation ?
L’encapsulation est le principe de « cacher les données ». Les détails internes d’une classe (attributs, méthodes auxiliaires) sont cachés à l’extérieur. L’utilisateur interagit uniquement via l’interface publique de la classe.
Avantages de l’encapsulation :
- Les données sont protégées contre une mauvaise utilisation ou des modifications non autorisées.
- La logique interne de la classe peut changer sans impacter le code externe.
- Améliore la sécurité, la maintenabilité et la testabilité.
2) Modificateurs d’accès
En C++, il existe trois types de modificateurs d’accès :
| Modificateur | Niveau d’accès | Utilisation |
|---|---|---|
public |
Accessible partout | Interface publique de la classe (API) |
protected |
Accessible uniquement dans la classe et les classes dérivées | Comportements communs utilisés en héritage |
private |
Accessible uniquement à l’intérieur de la classe | Champs de données, fonctions utilitaires |
3) Exemple d’encapsulation avec la classe Book
Voici un exemple d’une classe Book illustrant le principe d’encapsulation.
#include <iostream>
#include <string>
using namespace std;
class Book {
private:
string title;
string author;
double price;
int pageCount;
public:
// Constructeur
Book(string t, string a, double p, int pc)
: title(t), author(a), price(p), pageCount(pc) {}
// Getters et Setters
string GetTitle() const { return title; }
string GetAuthor() const { return author; }
double GetPrice() const { return price; }
void SetPrice(double p) {
if (p >= 0) price = p;
else cout << "Le prix ne peut pas être négatif !" << endl;
}
int GetPageCount() const { return pageCount; }
void Print() const {
cout << "Livre : " << title
<< " | Auteur : " << author
<< " | Pages : " << pageCount
<< " | Prix : " << price << " €" << endl;
}
};
int main() {
Book b("C++ Primer", "Lippman", 180.0, 950);
b.Print();
b.SetPrice(-50); // Invalide
b.SetPrice(210);
b.Print();
}
Les champs private ne peuvent pas être modifiés directement.
La modification doit se faire via des méthodes contrôlées comme SetPrice().
4) Champs protégés et héritage
Les classes dérivées peuvent accéder directement aux champs protected.
Cela permet à la sous-classe d’étendre ou de réutiliser les données de la classe de base.
class EBook : public Book {
protected:
string format; // Ex. : PDF, EPUB
public:
EBook(string t, string a, double p, int pc, string f)
: Book(t, a, p, pc), format(f) {}
void PrintWithFormat() const {
cout << GetTitle() << " (" << format << ")" << endl;
}
};
Ici, la classe dérivée ajoute une nouvelle propriété (format) sans violer le principe d’encapsulation.
5) Le mot-clé friend
Normalement, les membres private ne sont accessibles qu’à l’intérieur de la classe elle-même.
Cependant, il peut être nécessaire qu’une autre classe ou fonction accède à ces données privées.
Dans ce cas, on peut utiliser une déclaration friend.
class Book {
private:
string title;
double price;
public:
Book(string t, double p) : title(t), price(p) {}
// Déclaration d'une fonction amie
friend void ApplyDiscount(Book& b, double rate);
};
void ApplyDiscount(Book& b, double rate) {
b.price *= (1 - rate); // accès au membre privé
}
int main() {
Book b("Design Patterns", 300);
ApplyDiscount(b, 0.1);
}
L’utilisation de friend doit être faite avec précaution, car elle rompt l’encapsulation.
Elle ne devrait être utilisée que lorsque c’est nécessaire (par exemple : surcharge d’opérateurs, outils de débogage).
6) Principes de conception pour les getters et setters
- Utilisez des getters en ligne (
const) pour les accès simples aux données. - Effectuez toujours une validation dans les setters.
- Rendez les champs en lecture seule (getter uniquement) s’ils ne doivent pas être modifiés.
- Plutôt que de définir des getters/setters inutiles, abstraire le comportement directement dans des méthodes.
class Book {
private:
string title;
double price;
public:
Book(string t, double p) : title(t), price(p) {}
string GetTitle() const { return title; } // lecture seule
void ApplyDiscount(double rate) { price *= (1 - rate); } // comportement au lieu d’un setter
};
7) Sécurité des données avec le mot-clé const
Les méthodes const ne modifient pas l’état de la classe.
Cela soutient l’encapsulation et empêche les modifications inattendues.
class SafeBook {
private:
string title;
public:
SafeBook(string t) : title(t) {}
string GetTitle() const { return title; } // méthode const
void SetTitle(string t) { title = t; }
};
Une méthode Get écrite sans const viole ce principe et peut entraîner des erreurs inattendues.
8) Le modèle d’objet immuable (Immutable)
Conformément au principe d’encapsulation, certains objets sont conçus pour être « immuables ».
Tous leurs membres sont déclarés const et aucun setter n’est défini.
class ImmutableBook {
private:
const string title;
const string author;
const int pages;
public:
ImmutableBook(string t, string a, int p)
: title(t), author(a), pages(p) {}
string GetTitle() const { return title; }
string GetAuthor() const { return author; }
int GetPages() const { return pages; }
};
Cette approche garantit l’intégrité des données, notamment dans les environnements multithread.
9) Encapsulation et gestion de l’état des objets
L’encapsulation ne consiste pas seulement à contrôler l’accès, mais aussi à garantir que l’objet reste dans un état valide. L’état interne de la classe doit toujours avoir des valeurs cohérentes.
class ValidatedBook {
private:
double price;
public:
void SetPrice(double p) {
if (p < 0) throw invalid_argument("Le prix ne peut pas être négatif.");
price = p;
}
};
Ces mécanismes de validation garantissent que la classe reste dans un « état valide » et empêchent l’injection de données incorrectes depuis l’extérieur.
10) TL;DR
- L’encapsulation consiste à cacher les données et à y accéder via des comportements contrôlés.
private: accessible uniquement à l’intérieur de la classe.protected: accessible dans la classe et ses sous-classes.public: accessible partout (interface publique).friendpermet un accès externe aux données privées (à utiliser avec prudence).- La validation et l’utilisation de
constdans les getters/setters sont essentielles. - Les objets immuables assurent la sécurité des données.
- L’encapsulation est un principe fondamental pour des conceptions maintenables et fiables.
- Tous les exemples peuvent être exécutés avec Visual Studio 2022 ou GCC 11+.