Menu
Coddy logo textTech

Idiome 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 cachés du fichier d'en-tête.

L'idée centrale est simple : au lieu de déclarer des membres privés directement dans votre classe, vous déclarez un pointeur vers une classe d'implémentation qui n'est définie que 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 pare-feu de compilation et la compatibilité binaire. Lorsque vous modifiez la classe Impl, seul le fichier source nécessite une recompilation - pas tous les fichiers qui incluent l'en-tête. 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. C'est un piège courant lors de la première utilisation de Pimpl.

challenge icon

Défi

Facile

Construisons un gestionnaire de messages sécurisés en utilisant l'idiome Pimpl pour masquer les détails d'implémentation du chiffrement dans le fichier d'en-tête. Cela démontre comment Pimpl crée un pare-feu de compilation — quiconque inclut votre en-tête ne verra pas le fonctionnement interne du traitement de vos messages.

Vous organiserez votre code sur trois fichiers :

  • SecureMessage.h : Définissez l'interface publique pour votre classe SecureMessage.

    Votre classe doit avoir une classe Impl déclarée de manière anticipée (forward declaration) et un std::unique_ptr pointant vers celle-ci. L'interface publique doit inclure :

    • Un constructeur qui prend une const std::string& pour le message original
    • Un destructeur (doit être déclaré ici, défini dans le fichier .cpp)
    • Une méthode setKey(int key) pour définir une clé de chiffrement
    • Une méthode getEncrypted() qui retourne le message chiffré sous forme de std::string
    • Une méthode getOriginal() qui retourne le message original

    L'en-tête ne doit montrer que l'interface publique — aucun détail d'implémentation sur le fonctionnement du chiffrement ne doit être visible ici.

  • SecureMessage.cpp : Définissez la classe imbriquée Impl et implémentez toutes les méthodes.

    Votre classe Impl doit stocker le message original, la clé de chiffrement (par défaut à 0), et gérer la logique de chiffrement réelle. Utilisez un simple chiffre de César pour le chiffrement : décalez chaque caractère de la valeur de la clé. Par exemple, avec une clé de 3, 'a' devient 'd', 'z' boucle pour devenir '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 par défaut) car unique_ptr a besoin du type Impl complet.

  • main.cpp : Lisez deux entrées :
    1. Une chaîne de message (peut contenir des espaces)
    2. Une clé de chiffrement (entier)

    Créez un objet SecureMessage, définissez la clé et affichez les résultats :

    1. Affichez Original: suivi du message original
    2. Affichez Encrypted: suivi du message chiffré

Par exemple, avec les entrées hello world et 3 :

Original: hello world
Encrypted: khoor zruog

Avec les entrées xyz abc et 5 :

Original: xyz abc
Encrypted: cde fgh

Remarquez comment le fichier d'en-tête ne révèle rien sur l'implémentation du chiffre de César — c'est là toute la puissance de Pimpl. Si vous changiez plus tard pour un algorithme de chiffrement différent, seul SecureMessage.cpp nécessiterait une recompilation, et non les fichiers qui incluent l'en-tête.

Aide-mémoire

L'idiome Pimpl (Pointer to Implementation) masque les détails d'implémentation en les déplaçant vers une classe distincte définie uniquement dans le fichier source, créant ainsi un pare-feu de compilation.

Structure de base avec déclaration anticipée dans l'en-tête :

// Widget.h
#include <memory>

class Widget {
public:
    Widget();
    ~Widget();  // Doit être déclaré dans l'en-tête, défini dans le .cpp
    void doSomething();
    
private:
    class Impl;  // Déclaration anticipée
    std::unique_ptr<Impl> pImpl;
};

Implémentation dans le fichier source :

// Widget.cpp
#include "Widget.h"

class Widget::Impl {
public:
    int data = 42;
    void process() { /* implémentation */ }
};

Widget::Widget() : pImpl(std::make_unique<Impl>()) {}
Widget::~Widget() = default;  // Doit être défini dans le .cpp

void Widget::doSomething() { pImpl->process(); }

Points clés :

  • Le destructeur doit être défini dans le fichier source (même s'il est par défaut) car unique_ptr a besoin du type complet de Impl pour le supprimer
  • Les modifications apportées à Impl ne nécessitent que la recompilation du fichier source, et non des fichiers qui incluent l'en-tête
  • Fournit un pare-feu de compilation et une compatibilité binaire

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;
}
quiz iconTestez-vous

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