Idioma Pimpl
Parte da seção Programação Orientada a Objetos do Journey de C++ da Coddy. Lição 78 de 104.
O idioma Pimpl (Pointer to Implementation) é uma técnica que oculta os detalhes de implementação de uma classe, movendo-os para uma classe separada declarada antecipadamente. Isso reduz as dependências de compilação e mantém os membros privados verdadeiramente ocultos no arquivo de cabeçalho.
A ideia central é simples: em vez de declarar membros privados diretamente na sua classe, você declara um ponteiro para uma classe de implementação que é definida apenas no arquivo-fonte:
// Widget.h
#include <memory>
class Widget {
public:
Widget();
~Widget();
void doSomething();
private:
class Impl; // Declaração antecipada
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(); }Os principais benefícios são o firewall de compilação e a compatibilidade binária. Quando você altera a classe Impl, somente o arquivo-fonte precisa ser recompilado — não todos os arquivos que incluem o cabeçalho. Isso acelera drasticamente os tempos de compilação em projetos grandes.
Observe que o destrutor deve ser definido no arquivo-fonte (mesmo que seja padrão), porque unique_ptr precisa do tipo completo de Impl para excluí-lo. Essa é uma armadilha comum ao usar Pimpl pela primeira vez.
Desafio
FácilVamos criar um manipulador de mensagens seguro usando o idioma Pimpl para ocultar os detalhes de implementação da criptografia do arquivo de cabeçalho. Isso demonstra como o Pimpl cria uma barreira de compilação: qualquer pessoa que inclua seu cabeçalho não verá o funcionamento interno do processamento de mensagens.
Você organizará seu código em três arquivos:
SecureMessage.h: defina a interface pública para sua classeSecureMessage.Sua classe deve ter uma classe
Impldeclarada antecipadamente e umstd::unique_ptrpara ela. A interface pública deve incluir:- Um construtor que receba uma
const std::string¶ a mensagem original - Um destrutor (deve ser declarado aqui e definido no arquivo .cpp)
- Um método
setKey(int key)para definir uma chave de criptografia - Um método
getEncrypted()que retorne a mensagem criptografada como umastd::string - Um método
getOriginal()que retorne a mensagem original
O cabeçalho deve mostrar apenas a interface pública: nenhum detalhe de implementação sobre como a criptografia funciona deve estar visível aqui.
- Um construtor que receba uma
SecureMessage.cpp: defina a classe aninhadaImple implemente todos os métodos.Sua classe
Impldeve armazenar a mensagem original, a chave de criptografia (com valor padrão 0) e lidar com a lógica real de criptografia. Use uma cifra de César simples para a criptografia: desloque cada caractere pelo valor da chave. Por exemplo, com a chave 3, 'a' se torna 'd' e 'z' volta para 'c'.A criptografia deve afetar apenas letras minúsculas (a-z), deixando todos os outros caracteres inalterados. Lembre-se de definir o destrutor aqui (mesmo que seja padrão), pois
unique_ptrprecisa do tipoImplcompleto.main.cpp: leia duas entradas:- Uma string de mensagem (pode conter espaços)
- Uma chave de criptografia (inteiro)
Crie um objeto
SecureMessage, defina a chave e exiba os resultados:- Imprima
Original:seguido da mensagem original - Imprima
Encrypted:seguido da mensagem criptografada
Por exemplo, com as entradas hello world e 3:
Original: hello world
Encrypted: khoor zruogCom as entradas xyz abc e 5:
Original: xyz abc
Encrypted: cde fghObserve como o arquivo de cabeçalho não revela nada sobre a implementação da cifra de César: esse é o poder do Pimpl. Se mais tarde você mudar para um algoritmo de criptografia diferente, apenas SecureMessage.cpp precisará ser recompilado, e não qualquer arquivo que inclua o cabeçalho.
Experimente você mesmo
#include <iostream>
#include <string>
#include "SecureMessage.h"
int main() {
// Leia a mensagem (pode conter espaços)
std::string message;
std::getline(std::cin, message);
// Leia a chave de criptografia
int key;
std::cin >> key;
// TODO: Crie um objeto SecureMessage com a mensagem
// TODO: Defina a chave de criptografia
// TODO: Imprima "Original: " seguido da mensagem original
// TODO: Imprima "Encrypted: " seguido da mensagem criptografada
return 0;
}
Esta lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.
Todas as lições de Programação Orientada a Objetos
1Fundamentos de POO
Arquivos externosBuild e compilação em C++Arquivos de cabeçalho e arquivos-fonteNamespaces e escopoIntrodução à POO em C++Classes vs. objetosO ponteiro 'this'Métodos (funções membro)Atributos (membros de dados)Noções básicas de construtores e destruidoresRevisão - calculadora simples4Propriedades de Classe
Membros de Instância vs. EstáticosGetters e SettersFunções Membro ConstPalavra-chave MutableMétodos e Variáveis EstáticosFunções e Classes FriendRevisão - Gerenciador de Contas Bancárias7Herança
Herança básicaNíveis de acesso da herançaOrdem de chamada de construtor e destrutorSobrescrita de métodosFunções virtuais e VTableHerança múltiplaHerança virtualRevisão - Hierarquia de funcionários10Visão geral da STL
Visão geral e filosofia da STLContêineres da STLIteradoresAlgoritmos da STLFunctors e expressões lambdaRecapitulação - Frequência de palavras13Padrões de Projeto — Parte 1
Introdução aos Padrões de ProjetoPadrão SingletonFactory e Abstract FactoryPadrão BuilderPadrão ObserverPadrão Strategy2Gerenciamento de Memória
Memória Stack vs. HeapPonteiros e ReferênciasMemória Dinâmica (new/delete)Ponteiros Inteligentes em C++RAII em C++Revisão - Gerenciador de Array Dinâmico5Encapsulamento
Especificadores de acesso em C++Especificadores de acesso em profundidadeOcultação de informaçõesStruct vs ClasseClasses aninhadas e internasRecapitulação - Sistema de registros de alunos8Polimorfismo
Polimorfismo em compilação vs. execuçãoSobrecarga de funçõesFunções virtuais revisadasFunções virtuais purasClasses abstratasDesign de interfaces em C++Conversão dinâmica e RTTIRecapitulação — Calculadora de formas11Conceitos Avançados de OOP
Composição vs. HerançaMixins via CRTPIdioma PimplApagamento de TiposClasses Enum e Tipagem ForteTratamento de Exceções em OOPHierarquias de Exceções Personalizadas14Padrões de Projeto — Parte 2
Padrão CommandPadrão AdapterPadrão DecoratorPadrão Template MethodPadrão StatePadrão CompositeRAII como Padrão3Construtores e Destrutores
Construtor PadrãoConstrutor ParametrizadoConstrutor de CópiaConstrutor de MovimentoListas de Inicialização do ConstrutorConstrutores DelegantesAprofundamento em DestrutoresRegra dos Três / Cinco / ZeroRevisão - Classe String6Sobrecarga de Operadores
Introdução à Sobrecarga de OperadoresSobrecarga de Operadores AritméticosSobrecarga de Operadores de ComparaçãoOperadores de FluxoSobrecarga do Operador de AtribuiçãoSobrecarga dos Operadores [] e ()Operadores de Conversão de TipoRecapitulação - Classe Matrix9Templates
Templates de FunçõesTemplates de ClassesEspecialização de TemplatesTemplates VariádicosNoções Básicas de SFINAE e Traits de TiposRecapitulação - Contêiner GenéricoPratique por conta própria: Compilador de C++ online