Las clases abstractas y las funciones virtuales puras

Las clases abstractas están diseñadas para crear entidades genéricas, sobre cuya base se supone que se crearán clases derivadas más concretas. Una clase abstracta, es una clase que sólo puede ser utilizada como clase básica para alguna otra clase, por eso no se puede crear un objeto de tipo de clase abstracta.

La clase que contenga aunque sea solo una función virtual pura, es abstracta. Por eso las clases derivadas de una clase abstracta deben implementar todas sus funciones virtuales puras, de lo contrario, serán clases abstractas.

Una función virtual se declara como "pura" con la ayuda de la sintaxis de un especificador puro. Veremos como ejemplo la clase CAnimal, que se crea solo para proporcionar las funciones generales, los propios objetos del tipo CAnimal tienen un carácter demasiado general para la aplicación práctica. De esta forma, la clase CAnimal es un buen candidato a clase abstracta:

class CAnimal
  {
public:
                      CAnimal();     // constructor
   virtual void       Sound() = 0;   // función virtual pura
private:
   double             m_legs_count;  // número de patas del animal
  };

Aquí la función Sound() es virtual pura, por eso se la declara con el especificador de función virtual pura PURE (=0).

Las funciones virtuales puras son solo aquellas funciones virtuales para las que se indica el especificador puro  PURE, y  precisamente: (=NULL) o (=0). Ejemplo de declaración y uso de una clase abstracta:

class CAnimal
  {
public:
   virtual void       Sound()=NULL;   // PURE method, debe ser redefinido en la clase derivada, la propia clase CAnimal se ha convertido en abstracta y no puede ser creada
  };
//--- derivada de la clase abstracta
class CCat : public CAnimal
 {
public:
  virtual void        Sound() { Print("Myau"); } // PURE es redefinido, la clase CCat no es abstracta y puede ser creada
 };
 
//--- ejemplos de uso incorrecto
new CAnimal;         // error 'CAnimal' - el compilador retorna el error "cannot instantiate abstract class"
CAnimal some_animal; // error 'CAnimal' - el compilador retorna el error "cannot instantiate abstract class"
 
//--- ejemplos de uso correcto
new CCat;  // no hay error, la clase CCat no es abstracta
CCat cat;  // no hay error, la clase CCat no es abstracta

 
Limitaciones de uso de las clases abstractas

Si el constructor de una clase abstracta invoca una función virtual pura (directa o indirectamente) el resultado es indefinido.

//+------------------------------------------------------------------+
//| Clase básica abstracta                                           |
//+------------------------------------------------------------------+
class CAnimal
  {
public:
   //--- función virtual pura
   virtual void      Sound(void)=NULL;
   //--- función
   void              CallSound(void) { Sound(); }
   //--- constructor
   CAnimal()
    {
     //--- invocación directa del método virtual
     Sound();
     //--- invocación indirecta (a través de una tercera función)
     CallSound();
     //--- un constructor y/o destructor siempre invoca sus propias funciones,
     //--- a pesar del carácter virtual y de la redefinición de la función invocada en la derivada
     //--- si la función invocada es virtual pura, entonces
     //--- la invocación provocará el error de ejecución crítico: "pure virtual function call"
    }
  };

Sin embargo, los constructores y destructores de las clases abstractas pueden invocar otras funciones miembro.