Menu
Coddy logo textTech

Idiom Pimpl

Fa parte della sezione Programmazione orientata agli oggetti del percorso C++ di Coddy. Lezione 78 di 104.

L’idioma Pimpl (Pointer to Implementation) è una tecnica che nasconde i dettagli di implementazione di una classe spostandoli in una classe separata dichiarata in avanti. Questo riduce le dipendenze di compilazione e mantiene i membri privati realmente nascosti nel file header.

L'idea di base è semplice: invece di dichiarare direttamente i membri privati nella tua classe, dichiari un puntatore a una classe di implementazione definita solo nel file sorgente:

// Widget.h
#include <memory>

class Widget {
public:
    Widget();
    ~Widget();
    void doSomething();
    
private:
    class Impl;  // Dichiarazione anticipata
    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(); }

I principali vantaggi sono il firewall di compilazione e la compatibilità binaria. Quando modifichi la classe Impl, è necessario ricompilare solo il file sorgente, non tutti i file che includono l’header. Questo velocizza notevolmente i tempi di compilazione nei progetti di grandi dimensioni.

Nota che il distruttore deve essere definito nel file sorgente (anche se predefinito), perché unique_ptr ha bisogno del tipo completo di Impl per eliminarlo. Questa è una trappola comune quando si usa Pimpl per la prima volta.

challenge icon

Sfida

Facile

Creiamo un gestore di messaggi sicuro usando l'idioma Pimpl per nascondere i dettagli dell'implementazione della cifratura nel file header. Questo mostra come Pimpl crei una barriera di compilazione: chiunque includa il tuo header non vedrà il funzionamento interno dell'elaborazione dei messaggi.

Organizzerai il codice in tre file:

  • SecureMessage.h: definisci l'interfaccia pubblica della classe SecureMessage.

    La tua classe dovrebbe avere una classe Impl dichiarata in avanti e un std::unique_ptr a essa. L'interfaccia pubblica dovrebbe includere:

    • Un costruttore che accetta un const std::string& per il messaggio originale
    • Un distruttore (deve essere dichiarato qui e definito nel file .cpp)
    • Un metodo setKey(int key) per impostare una chiave di cifratura
    • Un metodo getEncrypted() che restituisce il messaggio cifrato come std::string
    • Un metodo getOriginal() che restituisce il messaggio originale

    L'header dovrebbe mostrare solo l'interfaccia pubblica: qui non dovrebbero essere visibili dettagli dell'implementazione della cifratura.

  • SecureMessage.cpp: definisci la classe annidata Impl e implementa tutti i metodi.

    La tua classe Impl dovrebbe memorizzare il messaggio originale, la chiave di cifratura (impostata su 0 per impostazione predefinita) e gestire la logica effettiva di cifratura. Usa un semplice cifrario di Cesare: sposta ogni carattere del valore della chiave. Per esempio, con la chiave 3, 'a' diventa 'd' e 'z' ricomincia da 'c'.

    La cifratura dovrebbe modificare solo le lettere minuscole (a-z), lasciando invariati tutti gli altri caratteri. Ricordati di definire qui il distruttore (anche se predefinito), perché unique_ptr richiede il tipo Impl completo.

  • main.cpp: leggi due input:
    1. Una stringa di messaggio (può contenere spazi)
    2. Una chiave di cifratura (intero)

    Crea un oggetto SecureMessage, imposta la chiave e visualizza i risultati:

    1. Stampa Original: seguito dal messaggio originale
    2. Stampa Encrypted: seguito dal messaggio cifrato

Per esempio, con gli input hello world e 3:

Original: hello world
Encrypted: khoor zruog

Con gli input xyz abc e 5:

Original: xyz abc
Encrypted: cde fgh

Nota che il file header non rivela nulla sull'implementazione del cifrario di Cesare: questo è il potere di Pimpl. Se in seguito passassi a un algoritmo di cifratura diverso, sarebbe necessario ricompilare solo SecureMessage.cpp, non i file che includono l'header.

Provalo tu

#include <iostream>
#include <string>
#include "SecureMessage.h"

int main() {
    // Leggi il messaggio (può contenere spazi)
    std::string message;
    std::getline(std::cin, message);
    
    // Leggi la chiave di crittografia
    int key;
    std::cin >> key;
    
    // TODO: Crea un oggetto SecureMessage con il messaggio
    
    // TODO: Imposta la chiave di crittografia
    
    // TODO: Stampa "Original: " seguito dal messaggio originale
    
    // TODO: Stampa "Encrypted: " seguito dal messaggio crittografato
    
    return 0;
}
quiz iconMettiti alla prova

Questa lezione include un breve quiz. Inizia la lezione per rispondere e tenere traccia dei tuoi progressi.

Tutte le lezioni di Programmazione orientata agli oggetti

Esercitati da solo: Compilatore C++ online