Menu
Coddy logo textTech

Idioma Pimpl

Parte de la sección Programación Orientada a Objetos del Journey de C++ de Coddy — lección 78 de 104.

El idioma Pimpl (Pointer to Implementation) es una técnica que oculta los detalles de implementación de una clase moviéndolos a una clase separada, declarada de forma anticipada. Esto reduce las dependencias de compilación y mantiene los miembros privados verdaderamente ocultos del archivo de cabecera.

La idea central es sencilla: en lugar de declarar miembros privados directamente en su clase, se declara un puntero a una clase de implementación que se define únicamente en el archivo de origen:

// Widget.h
#include <memory>

class Widget {
public:
    Widget();
    ~Widget();
    void doSomething();
    
private:
    class Impl;  // Declaración adelantada
    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(); }

Los beneficios clave son el cortafuegos de compilación y la compatibilidad binaria. Cuando cambias la clase Impl, solo el archivo fuente necesita recompilación, no todos los archivos que incluyen el encabezado. Esto acelera drásticamente los tiempos de compilación en proyectos grandes.

Ten en cuenta que el destructor debe definirse en el archivo de origen (incluso si es por defecto) porque unique_ptr necesita el tipo completo de Impl para eliminarlo. Este es un error común al usar Pimpl por primera vez.

challenge icon

Desafío

Fácil

Vamos a construir un manejador de mensajes seguros utilizando el idioma Pimpl para ocultar los detalles de implementación del cifrado del archivo de cabecera. Esto demuestra cómo Pimpl crea un cortafuegos de compilación: cualquier persona que incluya tu cabecera no verá el funcionamiento interno del procesamiento de tus mensajes.

Organizarás tu código en tres archivos:

  • SecureMessage.h: Define la interfaz pública para tu clase SecureMessage.

    Tu clase debe tener una clase Impl declarada de forma anticipada (forward-declared) y un std::unique_ptr hacia ella. La interfaz pública debe incluir:

    • Un constructor que reciba un const std::string& para el mensaje original
    • Un destructor (debe ser declarado aquí, definido en el archivo .cpp)
    • Un método setKey(int key) para establecer una clave de cifrado
    • Un método getEncrypted() que devuelva el mensaje cifrado como un std::string
    • Un método getOriginal() que devuelva el mensaje original

    La cabecera solo debe mostrar la interfaz pública; no debe haber detalles de implementación sobre cómo funciona el cifrado visibles aquí.

  • SecureMessage.cpp: Define la clase anidada Impl e implementa todos los métodos.

    Tu clase Impl debe almacenar el mensaje original, la clave de cifrado (por defecto 0) y manejar la lógica de cifrado real. Utiliza un cifrado César simple para el encriptado: desplaza cada carácter según el valor de la clave. Por ejemplo, con la clave 3, 'a' se convierte en 'd', 'z' da la vuelta a 'c'.

    El cifrado solo debe afectar a las letras minúsculas (a-z), dejando todos los demás caracteres sin cambios. Recuerda definir el destructor aquí (incluso si es por defecto) ya que unique_ptr necesita el tipo Impl completo.

  • main.cpp: Lee dos entradas:
    1. Una cadena de mensaje (puede contener espacios)
    2. Una clave de cifrado (entero)

    Crea un objeto SecureMessage, establece la clave y muestra los resultados:

    1. Imprime Original: seguido del mensaje original
    2. Imprime Encrypted: seguido del mensaje cifrado

Por ejemplo, con las entradas hello world y 3:

Original: hello world
Encrypted: khoor zruog

Con las entradas xyz abc y 5:

Original: xyz abc
Encrypted: cde fgh

Observa cómo el archivo de cabecera no revela nada sobre la implementación del cifrado César; ese es el poder de Pimpl. Si más adelante cambiaras a un algoritmo de cifrado diferente, solo SecureMessage.cpp necesitaría ser recompilado, no cualquier archivo que incluya la cabecera.

Hoja de referencia

El idioma Pimpl (Pointer to Implementation - Puntero a la Implementación) oculta los detalles de implementación moviéndolos a una clase separada definida solo en el archivo de origen, creando un cortafuegos de compilación.

Estructura básica con declaración anticipada (forward declaration) en el encabezado:

// Widget.h
#include <memory>

class Widget {
public:
    Widget();
    ~Widget();  // Debe declararse en el encabezado, definirse en .cpp
    void doSomething();
    
private:
    class Impl;  // Declaración anticipada
    std::unique_ptr<Impl> pImpl;
};

Implementación en el archivo de origen:

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

class Widget::Impl {
public:
    int data = 42;
    void process() { /* implementación */ }
};

Widget::Widget() : pImpl(std::make_unique<Impl>()) {}
Widget::~Widget() = default;  // Debe definirse en .cpp

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

Puntos clave:

  • El destructor debe definirse en el archivo de origen (incluso si es por defecto) porque unique_ptr necesita el tipo completo de Impl para eliminarlo
  • Los cambios en Impl solo requieren recompilar el archivo de origen, no los archivos que incluyen el encabezado
  • Proporciona un cortafuegos de compilación y compatibilidad binaria

Pruébalo tú mismo

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

int main() {
    // Lee el mensaje (puede contener espacios)
    std::string message;
    std::getline(std::cin, message);
    
    // Lee la clave de cifrado
    int key;
    std::cin >> key;
    
    // TODO: Crea un objeto SecureMessage con el mensaje
    
    // TODO: Establece la clave de cifrado
    
    // TODO: Imprime "Original: " seguido del mensaje original
    
    // TODO: Imprime "Encrypted: " seguido del mensaje cifrado
    
    return 0;
}
quiz iconPonte a prueba

Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.

Todas las lecciones de Programación Orientada a Objetos