Menu
Coddy logo textTech

Pimpl Idiom

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 esconde 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 do arquivo de cabeçalho.

A ideia central é simples: em vez de declarar membros privados diretamente em sua classe, você declara um ponteiro para uma classe de implementação que é definida apenas no arquivo de origem:

// 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 firewall de compilação e compatibilidade binária. Quando você altera a classe Impl, apenas o arquivo de origem precisa de recompilação — não todos os arquivos que incluem o cabeçalho. Isso acelera drasticamente os tempos de compilação em grandes projetos.

Observe que o destrutor deve ser definido no arquivo de origem (mesmo que seja o padrão) porque unique_ptr precisa do tipo completo de Impl para excluí-lo. Esta é uma armadilha comum ao usar Pimpl pela primeira vez.

challenge icon

Desafio

Fácil

Vamos construir 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 um firewall de compilação — qualquer pessoa que inclua seu cabeçalho não verá o funcionamento interno do processamento de sua mensagem.

Você organizará seu código em três arquivos:

  • SecureMessage.h: Defina a interface pública para sua classe SecureMessage.

    Sua classe deve ter uma classe Impl declarada antecipadamente (forward-declared) e um std::unique_ptr para ela. A interface pública deve incluir:

    • Um construtor que recebe uma const std::string& para a mensagem original
    • Um destrutor (deve ser declarado aqui, definido no arquivo .cpp)
    • Um método setKey(int key) para definir uma chave de criptografia
    • Um método getEncrypted() que retorna a mensagem criptografada como uma std::string
    • Um método getOriginal() que retorna a mensagem original

    O cabeçalho deve mostrar apenas a interface pública — nenhum detalhe de implementação sobre como a criptografia funciona deve ser visível aqui.

  • SecureMessage.cpp: Defina a classe Impl aninhada e implemente todos os métodos.

    Sua classe Impl deve armazenar a mensagem original, a chave de criptografia (padrão como 0) e lidar com a lógica de criptografia real. Use uma cifra de César simples para a criptografia: desloque cada caractere pelo valor da chave. Por exemplo, com a chave 3, 'a' torna-se 'd', '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 o padrão) já que o unique_ptr precisa do tipo Impl completo.

  • main.cpp: Leia duas entradas:
    1. Uma string de mensagem (pode conter espaços)
    2. Uma chave de criptografia (inteiro)

    Crie um objeto SecureMessage, defina a chave e exiba os resultados:

    1. Imprima Original: seguido pela mensagem original
    2. Imprima Encrypted: seguido pela mensagem criptografada

Por exemplo, com as entradas hello world e 3:

Original: hello world
Encrypted: khoor zruog

Com as entradas xyz abc e 5:

Original: xyz abc
Encrypted: cde fgh

Observe 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 você mudasse posteriormente para um algoritmo de criptografia diferente, apenas o SecureMessage.cpp precisaria de recompilação, não qualquer arquivo que inclua o cabeçalho.

Folha de consulta

O idioma Pimpl (Pointer to Implementation) oculta detalhes de implementação movendo-os para uma classe separada definida apenas no arquivo de origem, criando um firewall de compilação.

Estrutura básica com declaração antecipada (forward declaration) no cabeçalho:

// Widget.h
#include <memory>

class Widget {
public:
    Widget();
    ~Widget();  // Deve ser declarado no cabeçalho, definido no .cpp
    void doSomething();
    
private:
    class Impl;  // Declaração antecipada
    std::unique_ptr<Impl> pImpl;
};

Implementação no arquivo de origem:

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

class Widget::Impl {
public:
    int data = 42;
    void process() { /* implementação */ }
};

Widget::Widget() : pImpl(std::make_unique<Impl>()) {}
Widget::~Widget() = default;  // Deve ser definido no .cpp

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

Pontos-chave:

  • O destrutor deve ser definido no arquivo de origem (mesmo que seja o padrão/default) porque o unique_ptr precisa do tipo completo de Impl para excluí-lo
  • Alterações em Impl exigem apenas a recompilação do arquivo de origem, não dos arquivos que incluem o cabeçalho
  • Fornece firewall de compilação e compatibilidade binária

Experimente você mesmo

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

int main() {
    // Lê a mensagem (pode conter espaços)
    std::string message;
    std::getline(std::cin, message);
    
    // Lê a chave de criptografia
    int key;
    std::cin >> key;
    
    // TODO: Criar um objeto SecureMessage com a mensagem
    
    // TODO: Definir a chave de criptografia
    
    // TODO: Imprimir "Original: " seguido pela mensagem original
    
    // TODO: Imprimir "Encrypted: " seguido pela mensagem criptografada
    
    return 0;
}
quiz iconTeste seus conhecimentos

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