Idiom Pimpl
Fait partie de la section Programmation Orientée Objet du Journey C++ de Coddy. Leçon 78 sur 104.
L’idiome Pimpl (Pointer to Implementation) est une technique qui masque les détails d’implémentation d’une classe en les déplaçant dans une classe distincte déclarée au préalable. Cela réduit les dépendances de compilation et maintient les membres privés véritablement masqués du fichier d’en-tête.
L’idée fondamentale est simple : au lieu de déclarer directement les membres privés dans votre classe, vous déclarez un pointeur vers une classe d’implémentation qui est définie uniquement dans le fichier source :
// Widget.h
#include <memory>
class Widget {
public:
Widget();
~Widget();
void doSomething();
private:
class Impl; // Déclaration anticipée
std::unique_ptr<Impl> pImpl;
};// Widget.cpp
#include "Widget.h"
#include <iostream>
class Widget::Impl {
public:
int data = 42;
void process() { std::cout << "Processing: " << data << "\n"; }
};
Widget::Widget() : pImpl(std::make_unique<Impl>()) {}
Widget::~Widget() = default;
void Widget::doSomething() { pImpl->process(); }Les principaux avantages sont le compilation firewall et la compatibilité binaire. Lorsque vous modifiez la classe Impl, seul le fichier source doit être recompilé — et non chaque fichier qui inclut le header. Cela accélère considérablement les temps de compilation dans les grands projets.
Notez que le destructeur doit être défini dans le fichier source (même s’il est défini par défaut), car unique_ptr a besoin du type complet de Impl pour le supprimer. Il s’agit d’un piège courant lors de la première utilisation de Pimpl.
Défi
FacileConstruisons un gestionnaire de messages sécurisé en utilisant l’idiome Pimpl pour masquer les détails d’implémentation du chiffrement dans le fichier d’en-tête. Cela montre comment Pimpl crée une barrière de compilation : toute personne qui inclut votre fichier d’en-tête ne verra pas le fonctionnement interne du traitement de vos messages.
Vous allez organiser votre code sur trois fichiers :
SecureMessage.h: définir l’interface publique de votre classeSecureMessage.Votre classe doit avoir une classe
Impldéclarée au préalable ainsi qu’unstd::unique_ptrvers celle-ci. L’interface publique doit inclure :- Un constructeur qui prend un
const std::string&pour le message original - Un destructeur (qui doit être déclaré ici et défini dans le fichier .cpp)
- Une méthode
setKey(int key)pour définir une clé de chiffrement - Une méthode
getEncrypted()qui renvoie le message chiffré sous forme destd::string - Une méthode
getOriginal()qui renvoie le message original
Le fichier d’en-tête doit uniquement présenter l’interface publique : aucun détail d’implémentation sur le fonctionnement du chiffrement ne doit être visible ici.
- Un constructeur qui prend un
SecureMessage.cpp: définir la classe imbriquéeImplet implémenter toutes les méthodes.Votre classe
Impldoit stocker le message original, la clé de chiffrement (avec 0 comme valeur par défaut) et gérer la logique réelle du chiffrement. Utilisez un simple chiffrement de César : décalez chaque caractère selon la valeur de la clé. Par exemple, avec la clé 3, « a » devient « d » et « z » revient à « c ».Le chiffrement ne doit affecter que les lettres minuscules (a-z), en laissant tous les autres caractères inchangés. N’oubliez pas de définir le destructeur ici (même s’il est défini par défaut), car
unique_ptra besoin du typeImplcomplet.main.cpp: lire deux entrées :- Une chaîne représentant un message (qui peut contenir des espaces)
- Une clé de chiffrement (un entier)
Créez un objet
SecureMessage, définissez la clé et affichez les résultats :- Afficher
Original:suivi du message original - Afficher
Encrypted:suivi du message chiffré
Par exemple, avec les entrées hello world et 3 :
Original: hello world
Encrypted: khoor zruogAvec les entrées xyz abc et 5 :
Original: xyz abc
Encrypted: cde fghRemarquez que le fichier d’en-tête ne révèle rien de l’implémentation du chiffrement de César : c’est là toute la puissance de Pimpl. Si vous passiez ultérieurement à un autre algorithme de chiffrement, seul SecureMessage.cpp devrait être recompilé, et non chaque fichier qui inclut le fichier d’en-tête.
Essayez vous-même
#include <iostream>
#include <string>
#include "SecureMessage.h"
int main() {
// Lire le message (peut contenir des espaces)
std::string message;
std::getline(std::cin, message);
// Lire la clé de chiffrement
int key;
std::cin >> key;
// TODO: Créer un objet SecureMessage avec le message
// TODO: Définir la clé de chiffrement
// TODO: Afficher "Original: " suivi du message original
// TODO: Afficher "Encrypted: " suivi du message chiffré
return 0;
}
Cette leçon comprend un petit quiz. Commencez la leçon pour y répondre et suivre votre progression.
Toutes les leçons de Programmation Orientée Objet
1Fondamentaux de la POO
Fichiers externesGénération et compilation en C++Fichiers d’en-tête et fichiers sourceEspaces de noms et portéeIntroduction à la POO en C++Classes et objetsLe pointeur « this »Méthodes (fonctions membres)Attributs (membres de données)Bases des constructeurs et destructeursRécapitulatif - Calculatrice simple4Propriétés de classe
Membres d’instance et statiquesAccesseurs et mutateursFonctions membres constMot-clé mutableMéthodes et variables statiquesFonctions et classes amiesRécapitulatif - Gestionnaire de compte bancaire7Héritage
Héritage de baseNiveaux d’accès de l’héritageOrdre d’appel des constructeurs et destructeursRedéfinition des méthodesFonctions virtuelles et VTableHéritage multipleHéritage virtuelRécapitulatif : hiérarchie des employés10Vue d’ensemble de la STL
Vue d’ensemble et philosophie de la STLConteneurs de la STLItérateursAlgorithmes de la STLFoncteurs et expressions lambdaRécapitulatif - fréquence des mots13Modèles de conception, partie 1
Introduction aux modèles de conceptionModèle SingletonFabrique et fabrique abstraiteModèle BuilderModèle ObserverModèle Strategy2Gestion de la mémoire
Mémoire de pile ou de tasPointeurs et référencesMémoire dynamique (new/delete)Pointeurs intelligents en C++RAII en C++Récapitulatif - Gestionnaire de tableaux dynamiques5Encapsulation
Spécificateurs d’accès en C++Spécificateurs d’accès en profondeurMasquage de l’informationStruct vs classeClasses imbriquées et internesRécapitulatif - Système de gestion des dossiers étudiants8Polymorphisme
Polymorphisme à la compilation vs à l’exécutionSurcharge de fonctionsRetour sur les fonctions virtuellesFonctions virtuelles puresClasses abstraitesConception d’interfaces en C++Conversion dynamique et RTTIRécapitulatif - Calculateur de formes11Concepts avancés de la POO
Composition ou héritageMixins via CRTPIdiom PimplEffacement de typeClasses enum et typage fortGestion des exceptions en POOHiérarchies d’exceptions personnalisées3Constructeurs et destructeurs
Constructeur par défautConstructeur paramétréConstructeur de copieConstructeur de déplacementListes d’initialisation des constructeursConstructeurs déléguésApprofondissement des destructeursRègle des trois / cinq / zéroRécapitulatif - Classe String6Surcharge des opérateurs
Introduction à la surcharge des opérateursSurcharge des opérateurs arithmétiquesSurcharge des opérateurs de comparaisonOpérateurs de fluxSurcharge de l’opérateur d’affectationSurcharge des opérateurs [] et ()Opérateurs de conversion de typeRécapitulatif – Classe Matrix9Templates
Templates de fonctionsTemplates de classesSpécialisation des templatesTemplates variadiquesBases de SFINAE et des traits de typesRécapitulatif - Conteneur génériqueEntraînez-vous par vous-même : Compilateur C++ en ligne