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 (Puntero a la implementación) es una técnica que oculta los detalles de implementación de una clase trasladándolos a una clase independiente declarada previamente. Esto reduce las dependencias de compilación y mantiene los miembros privados verdaderamente ocultos del archivo de encabezado.

La idea central es sencilla: en lugar de declarar miembros privados directamente en tu clase, declaras un puntero a una clase de implementación que se define únicamente en el archivo de código fuente:

// Widget.h
#include <memory>

class Widget {
public:
    Widget();
    ~Widget();
    void doSomething();
    
private:
    class Impl;  // Declaración anticipada
    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 es necesario recompilar el archivo fuente, no cada archivo que incluye el header. 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 se usa el valor predeterminado) 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

Construyamos un gestor de mensajes seguro utilizando el idiom Pimpl para ocultar los detalles de implementación del cifrado del archivo de encabezado. Esto demuestra cómo Pimpl crea una barrera de compilación: cualquiera que incluya tu encabezado no verá el funcionamiento interno del procesamiento de mensajes.

Organizarás tu código en tres archivos:

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

    Tu clase debe tener una clase Impl declarada previamente y un std::unique_ptr que apunte a ella. La interfaz pública debe incluir:

    • Un constructor que reciba un const std::string& para el mensaje original
    • Un destructor (debe declararse aquí y definirse 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

    El encabezado solo debe mostrar la interfaz pública: aquí no deben ser visibles detalles de implementación sobre cómo funciona el cifrado.

  • 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 (con un valor predeterminado de 0) y encargarse de la lógica de cifrado real. Utiliza un cifrado César simple para el cifrado: desplaza cada carácter según el valor de la clave. Por ejemplo, con la clave 3, 'a' se convierte en 'd' y 'z' vuelve a empezar en 'c'.

    El cifrado solo debe afectar a las letras minúsculas (a-z), dejando todos los demás caracteres sin cambios. Recuerda definir aquí el destructor (aunque sea predeterminado), ya que unique_ptr necesita que el tipo Impl esté 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 encabezado 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 sería necesario recompilar SecureMessage.cpp, no ningún archivo que incluya el encabezado.

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

Practica por tu cuenta: Compilador de C++ online