Puntero opaco

En programación, se llama puntero opaco a un caso especial de tipo de dato opaco, un tipo de dato declarado como puntero a un registro o estructura de datos de tipo no especificado.

Los punteros opacos se encuentran en varios lenguajes de programación, incluidos Ada, C, C++ y Modula-2.

Si el lenguaje es de tipado fuerte, los programas y procedimientos que no tienen información adicional sobre un puntero opaco de tipo T, pueden aun así declarar variables, vectores y registros de tipo T, asignar valores de ese tipo y comparar si dichos valores son iguales. Sin embargo, no serán capaces de usar este tipo de puntero para hacer referencia a su contenido y solo podrán hacer cambios en él mediante llamadas a procedimientos que sí tengan conocimiento para ello.

Los punteros opacos son una manera de ocultar a clientes ordinarios los detalles de implementación de una interfaz, de manera que la pueda sufrir cambios sin necesidad de recompilar los módulos que hacen uso de ella. Esto también supone una ventaja para el programador, ya que se pueden crear interfaces sencillas y ocultar la mayor parte de los detalles en otro fichero. Esto es importante para proporcionar compatibilidad binaria a lo largo de diferentes versiones de una biblioteca compartida, por ejemplo.

Esta técnica está descrita en Design Patterns con el nombre de patrón Bridge. También se la ha llamado "clases handle",
 "Pimpl idiom" ("pointer to implementation" - puntero a la implementación), "Compiler firewall idiom" o "gato Cheshire", especialmente en la comunidad C++.

Ejemplos

Ada 
package Library_Interface is

   type Handle is limited private;

   -- Operaciones...

private
   type Hidden_Implementation;    -- Definido en el cuerpo del paquete
   type Handle is access Hidden_Implementation;
end Library_Interface;
El tipo Handle es un puntero opaco a la verdadera implementación, que no está definida en la especificación. Observe que el tipo no es solo privado (para prohibir a los clientes el acceso directo al tipo, permitido solo mediante las operaciones), sino también limitado (para evitar la copia de la estructura de datos, previniendo así referencias colgantes.
package body Library_Interface is

   type Hidden_Implementation is record
      ...    -- La implementación en sí puede ser cualquier cosa
   end record;

   -- Definición de las operaciones...

end Library_Interface;
A veces a estos tipos se los llama "tipos Taft" (por Tucker Taft, el diseñador principal de Ada 95) porque se introdujeron en la que se conoce como Taft Amendment (la Enmienda Taft sobre Ada 83.

C 
/* obj.h */

struct obj;

/*
 * El compilador considera struct obj como un tipo incompleto. Los tipos
 * incompletos se pueden usar en declaraciones.
 */

size_t obj_size(void);

int obj_setid(struct obj *, int);

int obj_getid(struct obj *, int *);
/* obj.c */

#include "obj.h"

struct obj {
    int id;
};

/*
 * El que realiza la llamada se encargará de hacer
 * la reserva de memoria.
 * Proporcionar sólo la información necesaria
 */

size_t
obj_size(void)
{
    return sizeof(struct obj);
}

int
obj_setid(struct obj *o, int i)
{
    if (o == NULL) return -1;
    o->id = i;
    return 0;
}

int
obj_getid(struct obj *o, int *i)
{
    if (o == NULL || i == NULL) return -1;
    *i = o->id;
    return 0;
}
Este ejemplo muestra la manera de conseguir el aspecto de ocultación de información (Encapsulamiento) de la Programación orientada a objetos usando el lenguaje C. Si alguien quisiera cambiar la declaración de struct obj, no sería necesario recompilar ningún otro módulo del programa que use el fichero de cabecera obj.h, a menos que también cambie la API.

C++ 
//header file:
class Handle {
public:
    Handle();                         // Constructor
    Handle(const Handle&);            // Constructor de copia
    Handle(Handle&&);                 // Constructor de movimiento
    Handle& operator=(const Handle&); // Operador de copia por asignación
    ~Handle();                        // Destructor
    // Other operations...

private:
    struct CheshireCat;               // No se define aquí
    CheshireCat* smile;               // Handle
};
//CPP file:
#include "handle.h"

struct Handle::CheshireCat {
    int a;
    int b;
};

Handle::Handle()
    : smile(new CheshireCat()) {
    // do nothing
}

Handle::Handle(const Handle& other)
    : smile(new CheshireCat(*other.smile)) {
    // do nothing
}

Handle::Handle(Handle&& other) 
    : smile(0)
{
    std::swap(smile, other.smile);
}

Handle& Handle::operator=(const Handle &other) {
    if(this != &other) {
        *smile = *(other.smile);
    }
    return *this;
}

Handle::~Handle() {
    delete smile;
}
Un tipo de puntero opaco que se usa a menudo en las declaraciones de clase de C++ es el d-pointer. Un d-pointer es el único dato miembro privado de la clase y apunta a una instancia de una estructura. Bautizado por Arnt Gulbrandsen de Trolltech, este método permite que las declaraciones de clase omitan miembros privados, excepto por el propio d-pointer.
 El resultado: (a) se oculta a la vista una mayor cantidad de la implementación de la clase; (b) si se añaden nuevos miembros a la estructura privada, eso no afecta la compatibilidad primaria; (c) el fichero de cabecera que contiene la declaración de la clase solo necesita incluir los otros ficheros necesarios para la interfaz de la clase, sin los de su implementación. Un beneficio adicional es que los ciclos de compilación se aceleran porque los ficheros de cabecera cambian con menos frecuencia. El d-pointer se usa ampliamente en las bibliotecas Qt (biblioteca) y KDE.

C# 
Véase el Patrón Private class data.

Véase también 
 Interfaz binaria de aplicaciones
 Handle

Referencias

Enlaces externos 

 El modismo Pimpl
 Compilation Firewalls
 El modismo Fast Pimpl
 D-Pointers — KDE TechBase
 Cuando "aplicas XOR a un puntero contra un número aleatorio", el resultado es un puntero "realmente opaco" .
 Making Pimpl Easy, Vladimir Batov

Tipos de datos
C++