Herencia y polimorfismo

Primero definimos una clase cObjeto genérico, que posee un nombre almacenado en un buffer.

//-----------------------------------------------------------------------------
// TALLER DE PROGRAMACION I
//
// Temas a ejemplificar: herencia, polimorfismo, orden de llamada de
//                       constructores y destructores, clases abstractas
//-----------------------------------------------------------------------------
#ifndef VIRTUAL_H
#define VIRTUAL_H

#include <cstring>
#include <iostream>
 //-----------------------------------------------------------------------------
// CLASE de OBJETO GENERAL
// tanto la clase base como la derivada tendran dos objetos de esta clase como
// miembros privados, a fin de verificar el orden de llamada de los
// constructores y destructores
//-----------------------------------------------------------------------------
class cObjeto {
  private:
  char nombre[ 32 ];
  public:
  // Si el metodo esta definido en la declaracion de la clase
  // implicitamente es declarada "inline"
  // Sin embargo el compilador es libre de ignorar esta sugerencia.
  cObjeto(const char *nombre) {
  // siempre usar strncpy (y las versiones de tama#o limitado - con 'n' en
  // el nombre - de las funciones de cstring) cuando el tama#o del buffer
  // no se sabe si es suficiente
  strncpy( this->nombre, nombre, sizeof(this->nombre)-1 );
  // nos aseguramos de que el string termine en \0 por si "nombre" era mas
  // largo que 0x100 chars
  this->nombre[sizeof(this->nombre)-1] = '\0';
  std::cout << "Constructor Objeto: " << this->nombre << "\n";
  }
  // Este tambien es inline
  ~cObjeto() {
    std::cout << "Destructor Objeto: " << this->nombre << "\n";
  }
};

Definimos una clase cBase con un par de atributos de tipo cObjeto

//-----------------------------------------------------------------------------
// CLASE BASE
//-----------------------------------------------------------------------------
class cBase {
  private:
  cObjeto objeto_base_1;
  cObjeto objeto_base_2;
  public:
  cBase(): objeto_base_1( "Objeto 1 de Base" ),
  objeto_base_2( "Objeto 2 de Base con un nombre muy largo que va a ser truncado" ) {
    std::cout << "Constructor de BASE\n";
  }
  // Los metodos virtuales nunca pueden ser "inline" porque se resuelven en
  // tiempo de ejecucion
  virtual ~cBase();
  // Otra forma de declarar un metodo "inline" es con el modificador
  // asi podemos definir un metodo inline por fuera de la declaracion de
  // la clase (pero debe estar la definicion en el .h para que sea efectivo)
  inline void funcion_A();
  inline void funcion_B();
  // Por mas que lo defina dentro de la clase, tampoco va a ser "inline" por ser virtual
  virtual void funcion_C() {
    std::cout << "Funcion C de BASE\n";
  }
  // Metodo virtual puro, directamente no tiene definicion, por lo tanto hace
  // que la clase sea abstracta (no se pueda instanciar)
  virtual void funcion_D() = 0;
};

// Como dijimos, los inline deben estar definidos en el .h
void cBase :: funcion_A() {
  std::cout << "Funcion A de BASE\n";
}

void cBase :: funcion_B() {
  std::cout << "Funcion B de BASE\n";
}

Definimos una clase derivada de cDerivada. A diferencia de la clase cBase, definimos los métodos en un archivo aparte.

//-----------------------------------------------------------------------------
// CLASE DERIVADA
//-----------------------------------------------------------------------------
class cDerivada : public cBase {
  private:
  cObjeto objeto_deriv_1;
  cObjeto objeto_deriv_2;
  public:
  cDerivada();
  ~cDerivada();
  void funcion_A();
  void funcion_D();
};

#endif // _VIRTUAL_H_

Y en un archivo virtual.cpp

//-----------------------------------------------------------------------------
// TALLER DE PROGRAMACION I
//
// Temas a ejemplificar: herencia, polimorfismo, orden de llamada de
//                       constructores y destructores, clases abstractas
//-----------------------------------------------------------------------------
#include "virtual.h"
// Implementacion de cBase (cosas no inline)
cBase :: ~cBase() {
  std::cout << "Destructor de BASE\n";
}

// Implementacion de cDerivada (cosas no inline)
cDerivada :: cDerivada() : cBase(),
 objeto_deriv_1( "Objeto 1 de Derivada" ),
 objeto_deriv_2( "Objeto 2 de Derivada" ) {
  std::cout << "Constructor de DERIVADA\n";
}

cDerivada :: ~cDerivada() {
  std::cout << "Destructor de DERIVADA\n";
}

void cDerivada :: funcion_A() {
  std::cout << "Funcion A de DERIVADA\n";
}

void cDerivada :: funcion_D() {
  std::cout << "Funcion D de DERIVADA\n";
}

En un archivo main.cpp escribimos una función main de pruebas.

int main( void ) {
  cDerivada derivada;
  cBase *base_ptr = &derivada;
  // funcion de base, redefinida en derivada
  derivada.funcion_A();
  base_ptr->funcion_A();
  // funcion de base, no redefinida en derivada
  derivada.funcion_B();
  base_ptr->funcion_B();
  // funcion virtual de base, no redefinida en derivada
  derivada.funcion_C();
  base_ptr->funcion_C();
  // funcion virtual pura de base, redefinida en derivada
  derivada.funcion_D();
  base_ptr->funcion_D();
  return( 0 );
}