© FOTOGRIN/Shutterstock.com

Como lenguaje de programación orientado a objetos, C++ frecuentemente se basa en la implementación de la herencia. Esta función le permite modificar y reutilizar atributos y otras características de una clase, así como reducir la cantidad de código que necesita. Continúe leyendo para saber exactamente qué es la herencia y los tipos de herencia en C++ que puede usar.

¿Qué es la herencia?

De la misma manera que podemos”heredar”ciertas características de nuestros padres, en C++, las clases pueden heredar miembros de otra clase. De esta forma, la clase cuyos miembros se heredan se define como clase base o padre, y la clase que hereda estas características se conoce como clase derivada, subclase o clase hija. Dado que esto permite que se reutilicen los miembros de la clase principal, estos miembros no necesitan ser redefinidos. Por lo tanto, puede ahorrar tiempo al implementar código innecesario.

Existen similitudes con la función amigo y la clase amigo, ya que permiten que funciones y clases accedan a los miembros de otras clases. Sin embargo, friend permite el acceso a miembros privados, mientras que la herencia no.

¿Cuáles son los tipos de herencia en C++?

Hay 5 tipos de herencia en C++. Estos son:

Herencia únicaHerencia multinivelHerencia múltipleHerencia híbridaHerencia jerárquica

Echemos un vistazo a cada uno de estos.

Herencia única

El método de herencia más simple es donde una clase hereda los miembros de otra clase. Esto se puede mostrar con el siguiente código:

#include class BaseClass { public: int x; }; class DerivedClass: public BaseClass{ public: int y; }; int main() { Clase Derivada obj; obj.x=10 obj.y=20 std::cout”Variable miembro de la clase base x=”std::endl; std::cout”Variable miembro de clase derivada y=”obk.y std::endl; devolver 0; }

Aquí, hemos definido la clase base como”BaseClass”, con el miembro”x”que tiene acceso público. La clase derivada se define como”DerivedClass”, que hereda públicamente de”BaseClass”. La propia clase derivada tiene un miembro público llamado”y”. Creamos una instancia de la clase derivada llamada”DerivedClass obj”y establecemos los valores de x e y. Usando este objeto, accedemos al miembro”x”de la clase principal, y los valores de x e y se imprimen en la consola como se muestra en la captura de pantalla.

Herencia única mostrada en un ejemplo.

©”TNGD”.com

Heredar miembros privados

Vale la pena señalar que la clase derivada podría heredar todos los miembros de la clase principal porque tienen acceso público. Sin embargo, si los miembros se declaran privados, estos no se pueden heredar directamente. Una forma de evitar esto es si se heredan ciertas funciones que actúan sobre estos miembros. De esta forma, puede acceder indirectamente a estos miembros. Tome el siguiente ejemplo:

class Base{ private: int a; public: Base(int a) a(a) {} int getA() const { return a; } }; clase Derivado: base pública { público: Derivado (int a): Base (A) {} }; int main() { Derivado d(5); std::cout”Valor del objeto derivado:”d.getA() std::endl; devolver 0; }

Aquí, hemos definido una clase principal como”Base”y una clase derivada como”Derivada”. El miembro”a”de la clase principal es privado, por lo que la clase derivada no puede acceder directamente a él. Pero tenemos un constructor en la clase base que toma el argumento entero de”a”y establece el miembro privado”a”. La clase derivada tiene un constructor en sí mismo,”Derivado(int a): Base (a) {}”que toma el argumento entero”a”y lo pasa al constructor de la clase base. Debido a que la función”getA()”se ha heredado, se logra el acceso indirecto al miembro”a”de la clase principal.

Usando la función”principal”, tomamos una instancia”d”de la clase derivada y defínala con un argumento de 5. Se llama a la función”getA()”en esta instancia y se imprime la salida. Esto se muestra en la captura de pantalla.

Cómo heredar miembros privados, mostrado en un ejemplo.

©”TNGD”.com

Clases privadas vs. protegidas

Valdría la pena en esta etapa distinguir entre clases privadas y protegidas. Si bien las clases derivadas solo pueden acceder indirectamente a los miembros de la clase privada, una clase derivada puede acceder por completo a los miembros de la clase protegida. Pero solo se puede acceder a ellos dentro de esta jerarquía de clases, y no fuera de ella (esto sería acceso público).

Herencia de solo método

Estos ejemplos también muestran la herencia de campos, donde el campo “a” ha sido heredado. Pero también puede tener herencia de solo método, donde se usa un método para definir el comportamiento de la clase. Un ejemplo de esto se muestra a continuación:

#inlude class Base { public: virtual void bar() { std::cout”Base::bar()”std::endl; } }; clase Derivado: public Base { public: virtual voide bar() { std::cout”Derived::bar()”std::endl; } }; int main() { Base* b=nuevo Derivado(); b->barra();//muestra”Derivado::bar()”delete b; devolver 0; }

Al igual que antes, tenemos una clase principal”Base”y una clase derivada”Derivado”. “Base” tiene un método virtual “bar()”. Esto se llama virtual ya que no tiene implementación en la clase principal y puede ser anulado por la clase derivada. La implementación si”bar()”se proporciona en la definición de clase derivada. Podemos ver que se crea un puntero “b” dentro de la función “main()”, y se inicializa con el objeto “Derivado”.”Derivado::bar()”anula”Base::bar()”, ya que es una función virtual. Se llama a la implementación dentro de la clase derivada, que imprime el resultado”Derivado::bar()”como se ve en la captura de pantalla.

Un ejemplo que muestra la herencia de solo método.

©”TNGD”.com

Herencia multinivel

Hay muchos casos en los que puede querer crear una clase derivada de una clase que ya se ha derivado de una clase principal. Esto se conoce como herencia multinivel. En cuanto a los tipos de herencia en C++, este es bastante intuitivo. Un ejemplo de ello es cuando trabaja con tipos de vehículos. En esta situación, puede tener una clase principal de”Vehículos”que define ciertos métodos y parámetros universales para los vehículos con los que está trabajando, como automóviles y motocicletas. Ambos podrían ser clases derivadas de esta clase principal, con sus propias propiedades específicas. Pero dado que puede tener tipos particulares de estos, podría crear clases derivadas como”PerformanceCar”que luego tendrían sus propias propiedades aplicables.

Podemos ilustrar cómo funciona esto con el siguiente código:

#incluye #incluye clase Vehículo { public: std::string type; std::color de cadena; }; coche de clase: vehículo público { público: std::string modelo; año int; }; class ElectricCar: public Car { public: int batteryCapacity; }; int main() { Coche eléctrico tesla; tesla.type=”Coche eléctrico”; tesla.color=”Rojo”; tesla.modelo=”Modelo S”; tesla.año=2021; tesla.batteryCapacity=100; std::cout”Tipo:”tesla.type std::endl; std::cout”Color:”tesla.color std::endl; std::cout”Modelo:”tesla.model std::endl; std::cout”Año:”tesla.year std::endl; std::cout”Capacidad de la batería:”tesla.batteryCapacity std::endl; devolver 0; }

En este ejemplo, tenemos la clase base”Vehículo”, la clase derivada”Coche”y otra clase derivada llamada”Coche eléctrico”. La clase”Vehicle”tiene los miembros”type”y”color”, la clase”Car”tiene los miembros”model”y la variable entera”year”, y la clase”ElectricCar”tiene la variable de miembro entera”batteryCapacity”. La función principal crea una instancia de la clase”ElectricCar”, configurando todas sus variables miembro heredadas con valores específicos. Estas variables luego se imprimen en la consola.

Herencia multinivel implementada en un ejemplo, junto con su salida.

©”TNGD”.com

Herencia múltiple

Al igual que podemos tener una clase derivada de una clase ya derivada, podemos tener una clase derivada de más de una clase base. Podemos demostrar esto continuando con nuestra analogía del automóvil:

#include #include class Vehicle { public: std::string type; std::color de cadena; }; class ElectricEngine { public: int batteryCapacity; }; clase SportsCar { public: int topSpeed; }; class ElectricSportsCar: public Vehicle, public ElectricEngine, public SportsCar { public: std::string model; año int; }; int main() { Coche deportivo eléctrico tesla; tesla.type=”Coche eléctrico”; tesla.color=”Rojo”; tesla.modelo=”Roadster”; tesla.año=2022; tesla.batteryCapacity=200; tesla.topSpeed ​​=250; std::cout”Tipo:”tesla.type std::endl; std::cout”Color:”tesla.color std::endl; std::cout”Modelo:”tesla.model std::endl; std::cout”Año:”tesla.year std::endl; std::cout”Capacidad de la batería:”tesla.batteryCapacity”kWh”std::endl; std::cout”Velocidad máxima:”tesla.topSpeed ​​”mph”std::endl; devolver 0; }

Tenemos tres clases aquí,”Vehículo”,”Motor eléctrico”y”Auto deportivo”. Todas estas son clases base y tienen sus propiedades heredadas por la clase derivada”ElectricSportsCar”. Como antes, cada una de las clases principales tiene sus propias variables. Se crea una instancia de “ElectricSportsCar”, se asignan valores a sus propiedades y luego se imprimen en la consola.

Un ejemplo de herencia múltiple.

©”TNGD”.com

Herencia jerárquica

Estamos llegando al final de los diferentes tipos de herencia en C++. Aquí es donde la situación es un poco más matizada. Aunque técnicamente la herencia multinivel crea una jerarquía, esto es diferente a lo que se conoce como herencia jerárquica. Si está creando varias clases derivadas de una clase principal, esto se conoce como herencia jerárquica. Esto es útil cuando necesita crear clases relacionadas a partir de una clase principal, pero cada una con sus propias propiedades. El siguiente código muestra este ejemplo.

class Vehicle { public: std::string type; std::color de cadena; }; coche de clase: vehículo público { público: std:: modelo de cadena; año int; }; camión de clase: vehículo público { público: capacidad int; };

Nuevamente, tenemos la clase principal”Vehículo”, y las clases”Automóvil”y”Camión”se derivan de ella. Ambas clases heredan las variables”tipo”y”color”, pero también agregan sus propias propiedades. A saber,”modelo”y”año”para la clase”Automóvil”y”capacidad”para la clase”Camión”. En la primera captura de pantalla podemos ver el código implementado con la función “main()”, con una instancia de cada clase creada y sus atributos establecidos. La segunda captura de pantalla muestra el resultado en la consola.

Herencia jerárquica implementada en una clase.

©”TNGD”.com

El resultado del ejemplo anterior.

©”TNGD”.com

Herencia híbrida

La situación se vuelve un poco más compleja cuando se trata de herencia híbrida. Aquí es donde los diferentes tipos de herencia en C++ se combinan en una jerarquía de clases. Si bien esto puede volverse complejo, a veces es necesario para las operaciones que debe realizar. Considere la siguiente situación:

#include #include class Vehicle { public: std::string type; std:: color de la cadena; }; clase Motor { público: int caballos de fuerza; }; coche de clase: vehículo público, motor público { público: std::string model; año int; }; class ElectricCar: public Car { public: int batteryCapacity; }; en principal () { ElectricCar tesla; tesla.type=”Coche eléctrico”; tesla.color=”Rojo”; tesla.modelo=”Modelo S”; tesla.caballos de fuerza=300; tesla.año=2021; tesla.batteryCapacity=100; tesla.type=”Coche eléctrico”; std::cout”Tipo:”tesla.type std::endl; std::cout”Color:”tesla.color std::endl; std::cout”Caballos de fuerza:”tesla.caballos de fuerza std::endl; std::cout”Modelo:”tesla.model std::endl; std::cout”Año:”tesla.year std::endl; std::cout”Capacidad de la batería:”tesla.batteryCapacity std::endl; devolver 0; }

Como antes, tenemos la clase principal”Vehículo”, pero también la clase principal”Motor”. Tenemos un ejemplo de herencia múltiple, donde la clase”Automóvil”se deriva de las clases”Vehículo”y”Motor”. La clase “ElectricCar” se deriva entonces de la clase “Car”, que es un ejemplo de herencia multinivel, ya que se trata de un tipo de automóvil más especializado. Por lo tanto, tenemos un escenario en el que se está produciendo una herencia híbrida. Como tal, la herencia híbrida puede ser útil para ayudar a simplificar y comprender relaciones complejas, así como para facilitar el mantenimiento del código.

La primera imagen ilustra la implementación del código, donde hemos creado una instancia del Clase”ElectricCar”con su conjunto de atributos de todas sus clases principales. La salida impresa se muestra en la segunda imagen.

Herencia híbrida implementada en una clase.

©”TNGD”.com

El resultado del ejemplo anterior.

©”TNGD”.com

Una nota sobre la ambigüedad

Un problema muy común que puede surgir cuando se trata de varias clases es el tema de la ambigüedad. Aquí es donde tiene variables miembro o funciones idénticas en dos o más clases. Entonces, el compilador puede tener errores, ya que no puede decidir qué miembro usar para la operación que está tratando de ejecutar. Considere el siguiente código:

#include class A { public: void foo() {std::cout”A:foo()”std::endl;} }; clase B { public: void foo() {std::cout”B::foo()”std::endl;} }; clase C: público A, público B { público: }; int principal() { C c; c.foo();//Error del compilador: llamada ambigua a foo() desde A y B devuelve 0; }

Aquí tenemos una función llamada “foo()” tanto en la clase “A” como en la clase “B”. Dado que la clase”C”heredó de ambas clases, se produce un error durante la compilación. Esto se puede resolver de diferentes maneras:

Cambiar el nombre de la función o variable en conflicto en la clase principal o derivada para evitar confusiones. Usar el operador de resolución de alcance (::) para especificar la clase que contiene la función miembro deseada. La herencia virtual funciona con una noción similar a la función virtual mencionada anteriormente. Esto puede permitir que una clase derivada herede de las clases principales sin duplicar la clase base que comparten estos padres. Anular la función en conflicto redefiniendo la función en la clase derivada con una implementación especializada. Se ilustra un error de ambigüedad.

©”TNGD”.com

Resumen

Hay 5 tipos principales de herencia en C++ que puede utilizar, cada uno con sus propios casos de uso específicos. Si bien la herencia única puede ser adecuada para operaciones relativamente simples, cuando se trabaja con objetos y relaciones complejos, otros tipos, como la herencia múltiple, multinivel, jerárquica e híbrida, pueden ser más apropiados. Estos pueden ser útiles para hacer que el código sea más intuitivo y fácil de mantener, así como para representar mejor la relación entre clases.

La herencia en C++ explicada, con ejemplos Preguntas frecuentes (FAQ) 

¿Qué son las clases principales y las clases derivadas?

Una clase principal, también conocida como clase base, es la clase de la que se hereda. Por otro lado, una clase derivada, subclase o clase secundaria hereda propiedades y atributos de la clase principal.

¿Qué es la herencia en C++?

La herencia es un proceso que le permite crear una clase derivada de una clase principal o base, que hereda propiedades y funciones miembro de la clase principal. Esto puede ser útil para representar relaciones complicadas y reducir la necesidad de repetir la implementación del código.

¿Cuáles son los diferentes tipos de herencia en C++?

Hay 5 tipos principales de herencia en C++: única, múltiple, multinivel, jerárquica e híbrida. Único y múltiple se refieren a una sola clase que se deriva de una o más de una clase base, respectivamente. Herencias multinivel significa cuando una clase se deriva de una clase derivada. La herencia jerárquica se refiere a cuando varias clases se derivan de una clase base, y la herencia híbrida significa que se usa una combinación de estos otros tipos.

¿Cómo se implementa la herencia en C++?

Para hacer que una clase herede de una clase principal, use dos puntos (:), seguido del especificador de acceso (público o protegido en este caso) y el nombre de la clase base.

¿Qué son los miembros públicos, protegidos y privados?

Los miembros públicos son completamente visibles y accesibles desde cualquier lugar dentro de un programa, y ​​se puede acceder a ellos mediante cualquier función dentro o fuera de la clase. Las clases derivadas pueden acceder a los miembros protegidos, por lo que se pueden heredar. Ninguna función fuera de la clase puede acceder a los miembros privados y son invisibles para las clases derivadas. Sin embargo, los miembros privados pueden ser indirectos si se hereda una función pública que actúa sobre ellos.

¿Cuál es la diferencia entre herencia pública, privada y protegida?

La herencia pública es donde todos los miembros públicos de la clase base se convierten en miembros públicos de la clase derivada, y de manera similar con los miembros protegidos y privados. La herencia protegida es donde se heredan tanto los miembros públicos como los protegidos, pero los miembros privados permanecen inaccesibles. La herencia privada significa que los miembros públicos y protegidos se convierten en miembros privados de la clase derivada y, como siempre, los miembros privados permanecen invisibles.

¿Cómo resuelve la ambigüedad en la herencia?

Puede resolver la ambigüedad usando el generador de resolución de alcance, usando la herencia virtual o cambiando el nombre o redefiniendo las funciones en conflicto.

By Henry Taylor

Trabajo como desarrollador back-end. Algunos me habréis visto en la conferencia de desarrolladores. Últimamente he estado trabajando en un proyecto de código abierto.