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.
Sfida
FacileCreiamo 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 classeSecureMessage.La tua classe dovrebbe avere una classe
Impldichiarata in avanti e unstd::unique_ptra 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 comestd::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.
- Un costruttore che accetta un
SecureMessage.cpp: definisci la classe annidataImple implementa tutti i metodi.La tua classe
Impldovrebbe 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_ptrrichiede il tipoImplcompleto.main.cpp: leggi due input:- Una stringa di messaggio (può contenere spazi)
- Una chiave di cifratura (intero)
Crea un oggetto
SecureMessage, imposta la chiave e visualizza i risultati:- Stampa
Original:seguito dal messaggio originale - Stampa
Encrypted:seguito dal messaggio cifrato
Per esempio, con gli input hello world e 3:
Original: hello world
Encrypted: khoor zruogCon gli input xyz abc e 5:
Original: xyz abc
Encrypted: cde fghNota 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;
}
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
1Fondamenti della programmazione orientata agli oggetti
File esterniBuild e compilazione in C++File header e file sorgenteNamespace e ambitoIntroduzione alla programmazione orientata agli oggetti in C++Classi e oggetti a confrontoIl puntatore 'this'Metodi (funzioni membro)Attributi (membri dati)Fondamenti di costruttori e distruttoriRiepilogo - Calcolatrice semplice4Proprietà delle classi
Membri di istanza e staticiGetter e setterFunzioni membro constParola chiave mutableMetodi e variabili staticiFunzioni e classi friendRiepilogo - Gestore di conti bancari7Ereditarietà
Ereditarietà di baseLivelli di accesso nell’ereditarietàOrdine di chiamata di costruttori e distruttoriRidefinizione dei metodiFunzioni virtuali e VTableEreditarietà multiplaEreditarietà virtualeRiepilogo - Gerarchia dei dipendenti2Gestione della memoria
Memoria Stack vs HeapPuntatori e riferimentiMemoria dinamica (new/delete)Puntatori intelligenti in C++RAII in C++Riepilogo - Gestore di array dinamico5Incapsulamento
Specificatori di accesso in C++Specificatori di accesso in dettaglioOccultamento delle informazioniStruct vs classClassi annidate e interneRiepilogo - Sistema di registrazione degli studenti8Polimorfismo
Polimorfismo a compile time e a runtimeOverload delle funzioniFunzioni virtuali: ripassoFunzioni virtuali pureClassi astratteProgettazione delle interfacce in C++Dynamic casting e RTTIRipasso: calcolatrice di forme11Concetti avanzati di OOP
Composizione vs ereditarietàMixin tramite CRTPIdiom PimplType ErasureEnum class e tipizzazione forteGestione delle eccezioni in OOPGerarchie personalizzate di eccezioni14Pattern di progettazione - Parte 2
Pattern CommandPattern AdapterPattern DecoratorPattern Template MethodPattern StatePattern CompositeRAII come pattern3Costruttori e distruttori
Costruttore predefinitoCostruttore con parametriCostruttore di copiaCostruttore di spostamentoListe di inizializzazione dei costruttoriCostruttori delegantiApprofondimento sui distruttoriRegola del Tre / Cinque / ZeroRipasso - classe String6Sovraccarico degli operatori
Introduzione al sovraccarico degli operatoriSovraccarico degli operatori aritmeticiSovraccarico degli operatori di confrontoOperatori di flussoSovraccarico dell'operatore di assegnazioneSovraccarico degli operatori [] e ()Operatori di conversione di tipoRipasso - Classe Matrix9Template
Template di funzioneTemplate di classeSpecializzazione dei templateTemplate variadiciBasi di SFINAE e dei trait di tipoRiepilogo - Contenitore genericoEsercitati da solo: Compilatore C++ online