Mecanismos de Herencia y Herencia Virtual en C++

La herencia en C++ es un pilar fundamental de la programación orientada a objetos que permite estructurar el código creando jerarquías de clases. A través de este mecanismo, una clase derivada puede adquirir los atributos y comportamientos de una clase base, fomentando la reutilización y facilitando la extensión del sistema.

1. Estructura Fundamental

En este modelo, la clase original se denomina clase base (o superclase), mientras que la nueva clase que extiende su funcionalidad se conoce como clase derivada (o subclase). La sintaxis para establecer esta relación utiliza el operador de resolución implícito mediante dos puntos:

class EntidadPrincipal {
    // Atributos y métodos base
};

class EntidadEspecializada : public EntidadPrincipal {
    // Extensión de la entidad
};

2. Modificadores de Acceso en la Herencia

El modo en que se heredan los miembros de la clase base está determinado por el especificador de acceso utilizado en la declaración de la clase derivada:

  • Herencia pública (public): Los miembros públicos y protegidos de la base conservan su nivel de visibilidad en la derivada.
  • Herencia protegida (protected): Tanto los miembros públicos como los protegidos de la base pasan a ser protegidos en la subclase.
  • Herencia privada (private): Todos los meimbros heredados (públicos y protegidos) se vuelven inaccesibles desde fuera, convirtiéndose en privados dentro de la clase derivada.
class Componente { 
    // Definición de miembros
};

class SistemaPublico : public Componente { /* Mantiene accesos */ };
class SistemaProtegido : protected Componente { /* Todo pasa a ser protegido */ };
class SistemaPrivado : private Componente { /* Todo pasa a ser privado */ };

3. Ciclo de Vida: Inicialización y Destrucción

El compilador de C++ gestiona de forma estricta el orden de ejecución de los constructores y destructores en las jerarquías de clases. Durante la instanciación, se invoca primero el constructor de la clase base y posteriormente el de la derivada. Por el contrario, al liberar la memoria, el proceso se invierte: el destructor de la clase derivada actúa antes que el de la base.

4. Polimorfismo y Resolución Dinámica

El polimorfismo se materializa mediante el uso de funciones virtuales. Al declarar un método con la palabra clave virtual en la clase base, se delega la resolución de la llamada al tiempo de ejecución, permitiendo que cada subclase proporcione su propia implementación.

class Reporte {
public:
    virtual void generar() {
        std::cout << "Generando reporte base" << std::endl;
    }
};

class ReporteFinanciero : public Reporte {
public:
    void generar() override {
        std::cout << "Generando balance financiero" << std::endl;
    }
};

Aunque se utilice un puntero de tipo Reporte que apunte a un objeto ReporteFinanciero, la ejecución resolverá correctamente la versión especializada del método.

5. Redefinición frente a Ocultamiento

  • Redefinición (Overriding): Ocurre cuando una clase derivada proporciona una implementación específica para un método virtual heredado.
  • Ocultamiento (Hiding): Si la subclase declara un método con el mismo nombre que uno de la clase base, pero sin que este último sea virtual (o con una firma diferente), el método original queda eclipsado y no será accesible directamente sin calificación de ámbito.

6. Herencia Múltiple

Una de las características distintivas de C++ es la capacidad de que una clase herede de múltiples bases simultáneamente, combinando distintas interfaces.

class Volador {
    // Métodos para vuelo
};

class Navegador {
    // Métodos de orientación
};

class Drone : public Volador, public Navegador {
    // Integra capacidades de ambas clases
};

7. El Problema del Diamante y la Herencia Virtual

La herencia múltiple introduce una ambigüedad estructural conocida como el "problema del diamante". Esto sucede cuando una clase hereda de dos superclases que, a su vez, derivan de un ancestro común. Sin intervención, el objeto final contendrá dos copias de los miembros de la clase raíz.

class Dispositivo {
public:
    int id;
};

class Telefono : public Dispositivo {};
class Camara : public Dispositivo {};

// Ambigüedad: Smartphone tendría dos instancias de 'id'
class Smartphone : public Telefono, public Camara {};

Para garantizar que solo exista una única instancia de la clase base compartida, C++ emplea la herencia virtual. Al preceder el especificador de acceso con virtual, el compilador modifica el diseño del objeto para apuntar a una única subobjeto base.

class Telefono : virtual public Dispositivo {};
class Camara : virtual public Dispositivo {};

// Resolución: Solo existe una copia de Dispositivo en Smartphone
class Smartphone : public Telefono, public Camara {};

Ejemplo Práctico Integrado

A continuación, se ilustra cómo una clase especializada hereda comportamientos generales y añade su propia lógica:

#include <iostream>

class Electrodomestico {
public:
    void encender() {
        std::cout << "Dispositivo encendido." << std::endl;
    }
    void apagar() {
        std::cout << "Dispositivo apagado." << std::endl;
    }
};

class Lavadora : public Electrodomestico {
public:
    void lavar() {
        std::cout << "Iniciando ciclo de lavado..." << std::endl;
    }
};

int main() {
    Lavadora miLavadora;
    miLavadora.encender();
    miLavadora.lavar();
    miLavadora.apagar();
    return 0;
}

En este escenario, la instancia miLavadora aprovecha los métodos encender y apagar definidos en Electrodomestico, complementándolos con su función exclusiva lavar.

Etiquetas: C++ herencia Herencia Virtual polimorfismo oop

Publicado el 6-4 20:12