Menu
Coddy logo textTech

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.

challenge icon

Desafio

Fácil

Vamos 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 classe SecureMessage.

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

    • Um construtor que receba uma const std::string& para 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 uma std::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.

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

    Sua classe Impl deve 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_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 da mensagem original
    2. Imprima Encrypted: seguido da 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 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;
}
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

Pratique por conta própria: Compilador de C++ online