Imaginen esta "interfaz" (clase abstracta):
class IPortero {Tenemos otra clase Ascensor que podemos "personalizar" con nuestro propio portero, así nuestra implementación de portero puede hacer lo que se le de la gana:
public:
virtual IPortero() { }
virtual void ir_a_piso(int piso) = 0;
virtual int piso_destino() = 0;
};
class Ascensor {Con programación genérica, podemos reformular la interfaz convirtiéndola en un "concepto" y la clase Ascensor en una clase plantilla:
IPortero* m_portero;
public:
Ascensor(IPortero* portero) {
m_portero = portero;
}
void apretaron_boton_en_piso(int piso) {
m_portero->ir_a_piso(piso);
mover_ascensor(m_portero->piso_destino());
}
void mover_ascensor(int piso) { ... }
};
template<class TipoPortero>La pregunta es, ¿qué demonios es TipoPortero?. La respuesta es sencilla: TipoPortero puede ser cualquier tipo de dato que cumpla los siguientes requisitos:
class Ascensor {
TipoPortero m_portero;
public:
Ascensor() { }
void apretaron_boton_en_piso(int piso) {
m_portero.ir_a_piso(piso);
mover_ascensor(m_portero.piso_destino());
}
void mover_ascensor(int piso) { ... }
};
- Tenga un constructor por omisión (que se pueda construir un nuevo TipoPortero sin argumentos, o sea, TipoPortero()).
- Tenga una función miembro TipoPortero::ir_a_piso(int), la cual recibe un "int" (o cualquier tipo de dato que se pueda construir desde un "int" implícitamente).
- Y otra función miembro TipoPortero::piso_destino() que devuelve un entero.
¿Cómo especificamos la "interfaz" o los "requerimientos" de un concepto? Sencillamente no se puede, C++0x iba a soportar esto, pero ya no. Hoy en día la mejor respuesta es usar algunos comentarios en la clase Ascensor que especifiquen qué espera en sus parámetros de template. En este aspecto se podría decir que IPortero es mejor porque especifica explícitamente lo que el portero tiene que hacer (funciones a implementar, etc.).
¿Qué ventaja tiene el concepto con respecto a las interfaces? La clase genérica Ascensor ahora tiene el mismo portero adentro suyo (no un puntero a la interfaz). Las llamadas a las funciones miembro ir_a_piso y piso_destino son llamadas directas (no tienen el overhead de una llamada a una función virtual).