5.6.2. Reducir la recompilación

Su entorno de programación provocará una recompilación de un fichero si este se modifica, o si se modifica otro fichero del que depende, es decir, un archivo de cabecera que se haya incluido. Esto significa que cada vez que se haga un cambio en una clase, ya sea a la interfaz pública o a las declaraciones de los miembros privados, se provocará una recompilación de todo lo que incluya ese archivo de cabecera. Este efecto se conoce usualmente como el problema de la clase-base frágil. Para un proyecto grande en sus comienzos esto puede ser un gran problema pues la implementación suele cambiar a menudo; si el proyecto es muy grande, el tiempo de las compilaciones puede llegar a ser un gran problema.

La técnica para resolver esto se llama a veces clases manejador o el «gato de Chesire» [53] - toda la información sobre la implementación desaparece excepto por un puntero, la "sonrisa". El puntero apunta a una estructura cuya definición se encuentra en el fichero de implementación junto con todas las definiciones de las funciones miembro. Así, siempre que la interfaz no se cambie, el archivo de cabecera permanece inalterado. La implementación puede cambiar a su gusto, y sólo el fichero de implementación deberá ser recompilado y reenlazado con el proyecto.

Aquí hay un ejemplo que demuestra como usar esta técnica. El archivo de cabecera contiene solo la interfaz publica y un puntero de una clase especificada de forma incompleta:

//: C05:Handle.h
// Handle classes
#ifndef HANDLE_H
#define HANDLE_H

class Handle {
  struct Cheshire; // Class declaration only
  Cheshire* smile;
public:
  void initialize();
  void cleanup();
  int read();
  void change(int);
};
#endif // HANDLE_H ///:~

Listado 5.8. C05/Handle.h


Esto es todo lo que el programador cliente puede ver. La linea

struct Cheshire;

es una especificación de tipo incompleta o una declaración de clase (una definición de clase debe incluir el cuerpo de la clase). Le dice al compilador que Chesire es el nombre de una estructura, pero no detalles sobre ella. Esta es información suficiente para crear un puntero a la estructura; no puede crear un objeto hasta que el cuerpo de la estructura quede definido. En esta técnica, el cuerpo de la estructura está escondido en el fichero de implementación:

//: C05:Handle.cpp {O}
// Handle implementation
#include "Handle.h"
#include "../require.h"

// Define Handle's implementation:
struct Handle::Cheshire {
  int i;
};

void Handle::initialize() {
  smile = new Cheshire;
  smile->i = 0;
}

void Handle::cleanup() {
  delete smile;
}

int Handle::read() {
  return smile->i;
}

void Handle::change(int x) {
  smile->i = x;
} ///:~

Listado 5.9. C05/Handle.cpp


Chesire es una estructura anidada, así que se debe ser definido con resolución de ámbito:

struct Handle::Cheshire {

En Handle::initialize(), se solicita espacio de almacenamiento para una estructura Chesire, y en Handle::cleanup() se libera ese espacio. Este espacio se usa para almacenar todos los datos que estarían normalmente en la sección privada de la clase. Cuando compile Handle.cpp, esta definición de la estructura estará escondida en el fichero objeto donde nadie puede verla. Si cambia los elementos de Chesire, el único archivo que debe ser recompilado es Handle.cpp pues el archivo de cabecera permanece inalterado.

El uso de Handle es como el uso de cualquier clase: incluir la cabecera, crear objetos, y mandar mensajes.

//: C05:UseHandle.cpp
//{L} Handle
// Use the Handle class
#include "Handle.h"

int main() {
  Handle u;
  u.initialize();
  u.read();
  u.change(1);
  u.cleanup();
} ///:~

Listado 5.10. C05/UseHandle.cpp


La única cosa a la que el programador cliente puede acceder es a la interfaz publica, así que mientras la implementación sea lo único que cambie, el fichero anterior no necesita recompilarse. Así, aunque esto no es ocultación de implementación perfecta, es una gran mejora.



[53] Este nombre se le atribuye a John Carolan, uno de los pioneros del C++, y por supuesto, Lewis Carroll. Esta técnica se puede ver también como una forma del tipo de diseño «puente», descrito en el segundo volumen.