Herencia Múltiple en C++: Implementación y Ejemplos Prácticos
Resumen
¿Qué es la herencia múltiple en la programación orientada a objetos?
La herencia es uno de los pilares fundamentales de la programación orientada a objetos, permitiendo que las clases hereden características y comportamientos de otras. Pero, ¿qué pasa cuando una clase necesita heredar de más de una clase base? Aquí es donde entra en juego la herencia múltiple, un concepto poderoso que C++ implementa para ofrecer una mayor flexibilidad en la construcción de programas.
¿Cómo se implementa la herencia múltiple en C++?
La herencia múltiple en C++ permite que una clase derive de más de una clase base. Para ilustrar cómo se implementa, consideremos un ejemplo donde tenemos una clase base Animal y dos clases derivadas Herbívor y Carnívor. Si queremos crear un Omnívoro, que herede las propiedades de ambos, herbívoro y carnívoro, podemos hacerlo mediante la herencia múltiple de la siguiente manera:
classOmni:publicHerb,publicCarn{public:Omni():Herb(),Carn(){// Implementación del constructor}};
¿Cómo manejar conflictos y ambigüedades?
Cuando una clase hereda de múltiples clases base, puede surgir la ambigüedad, especialmente si las clases base tienen métodos o propiedades con el mismo nombre. C++ aborda esta situación permitiendo especificar de qué clase base se debe llamar un método. Por ejemplo:
Omni o;o.Herb::comer();// Llama a la versión del método `comer` de la clase Herbo.Carn::comer();// Llama a la versión del método `comer` de la clase Carn
¿Cómo redefinir métodos en una clase derivada?
Para eliminar completamente la ambigüedad y personalizar el comportamiento de una clase que utiliza herencia múltiple, se puede redefinir un método en la clase derivada. Imaginemos que queremos que Omni tenga su propia versión del método comer:
classOmni:publicHerb,publicCarn{public:Omni():Herb(),Carn(){}voidcomer(){ std::cout <<"Este animal come lo que sea."<< std::endl;}};
De esta forma, al invocar el método comer de un objeto de tipo Omni, se ejecutará la versión personalizada, eliminando toda ambigüedad.
¿Qué más puedo aprender sobre herencia y programación orientada a objetos?
La herencia múltiple es solo uno de los muchos conceptos avanzados en programación orientada a objetos. Si te interesa profundizar más, considera explorar cursos y materiales educativos adicionales sobre estos temas. La plataforma Platzi, por ejemplo, ofrece cursos detallados sobre programación orientada a objetos, que abordan desde los fundamentos hasta técnicas más avanzadas del diseño de software.
Un enfoque robusto del aprendizaje y la práctica continua te llevará a entender profundamente no solo la herencia múltiple, sino también otros conceptos como el polimorfismo, las interfaces y el encapsulamiento. ¡Sigue explorando, y pronto dominarás los secretos de la programación orientada a objetos!
#include <iostream>#include <string>using namespace std;classAnimal{protected:static int numero_animales; string alimento;public:Animal();~Animal();static int obtenerNumeroAnimales(); string obtenerAlimento(){return alimento;};voidcomer(){ cout<<"Este animal está comiendo "<<alimento<<"... ñom ñom"<<endl;};};int Animal::numero_animales =0;Animal::Animal(){ cout<<"Creando nuevo animal ... "<<endl; numero_animales +=1;}Animal::~Animal(){ cout<<"Borrando animal..."<<endl; numero_animales-=1;}int Animal::obtenerNumeroAnimales(){return numero_animales;}classHerviboro:publicAnimal{public:Herviboro():Animal(){this->alimento ="plantas ";}voidpastar(){ cout<<"Este animal está pasteando ..."<<endl;}};classCarnivoro:publicAnimal{public:Carnivoro():Animal(){this->alimento ="plantas ";}voidcazar(){ cout<<"Este animal está cazando ..."<<endl;}};classOmnivoro:publicHerviboro,publicCarnivoro{public:Omnivoro():Herviboro(),Carnivoro(){}voidcomer(){ cout<<"Este animal come lo que sea ...."<<endl;}};int main(){Animal*a =newAnimal();Herviboro*h =newHerviboro();Carnivoro*c =newCarnivoro();Omnivoro*o =newOmnivoro(); cout<<"Numero de animeles: "<<Animal::obtenerNumeroAnimales()<<endl; a->comer(); h->pastar(); h->comer(); c->cazar(); c->comer(); cout<<"omnivoro"<<endl; o->comer();delete a; cout<<"Numero de animales"<<Animal::obtenerNumeroAnimales()<<endl;}
Hola, quería comentar que en la clase Omnivoro algo no quedo bien. Si nos fijamos cuenta 5 animales, cuando deberían ser 4.
Entiendo que algo del constructor no quedo bien, ya que esta pasando 2 veces por el constructor Animal.
No se si estoy en lo correcto.
Lo que está pasando es que en C++ la herencia funciona así, un omnivoro está siendo literalmente un hervívoro y un carnívoro a la vez, cuando creas un omnívoro, en realidad por debajo creas un hervívoro y un omnívoro en memoria, por eso cuenta 2.
Hay formas de cambiar este resultado, una sería sobre escribir los conteos, otra, heredar directo de Animal, y probablemente haya otras formas más.
Pero, para fines de la clase, lo que te pasa es correcto.
Tenia la misma duda. Llevaba analizando bastante el código. Gracias por la aclaración.
classOmnivoro:publicHerviboro,publicCarnivoro{public:Omnivoro():Herviboro(),Carnivoro(){}voidcomer(){ cout <<"Este animal como lo que sea..."<< endl;}};int main(){Animal*a =newAnimal();Herviboro*h =newHerviboro();Carnivoro*c =newCarnivoro();Omnivoro*o =newOmnivoro(); cout <<"Numero de animales "<<Animal::obtenerNumeroAnimales()<< endl; a->comer(); h->pastar(); h->comer(); c->cazar(); c->comer(); cout <<"Omnivoro"<< endl; o->comer();delete a; cout <<"Numero de animales "<<Animal::obtenerNumeroAnimales()<< endl;}
#include <iostream>#include <conio.h>#include <stdlib.h>#include <string>#include <locale.h>using namespace std;classMounstro{protected:static int numeroMounstros; string alimento;public:Mounstro();~Mounstro();static int obtenerNumeroMounstros(); string obtenerAlimento(){return alimento;}voidcomer(){ cout <<"Este mounstro está comiendo "<< alimento <<" rico, rico "<< endl;}};int Mounstro::numeroMounstros =0;Mounstro::Mounstro(){ cout <<"Creando nuevo mounstro...."<< endl; numeroMounstros +=1;}int Mounstro::obtenerNumeroMounstros(){return numeroMounstros;}Mounstro::~Mounstro(){ cout <<"borrando bicho...."<< endl; numeroMounstros -=1;}classHerviboro:publicMounstro{public:Herviboro():Mounstro(){this->alimento ="plantas";}voidpastar(){ cout <<"Este animal está pastando... "<< endl;}};classCarnivoro:publicMounstro{public:Carnivoro():Mounstro(){this->alimento ="carne";}voidcazar(){ cout <<"Este animal está cazando..."<< endl;}};classOmnivoro:publicHerviboro,publicCarnivoro{public:Omnivoro():Herviboro(),Carnivoro(){}voidcomer(){ cout <<"este animal come cualquier cosa "<< endl;}}; int main(){setlocale(LC_ALL,"");Mounstro* m =newMounstro();Herviboro* h =newHerviboro();Carnivoro* c =newCarnivoro();Omnivoro* o =newOmnivoro(); cout <<"Numero de mounstros: "<<Mounstro::obtenerNumeroMounstros()<< endl; m->comer(); h->pastar(); h->comer(); c->cazar(); c->comer(); cout <<"omnivoro"<< endl; o->comer();delete m; cout <<"Numero de mounstros: "<<Mounstro::obtenerNumeroMounstros()<< endl;getch();};
Gracias!!!
Algo que aclarar que cuando se crea un Omnívoro, como llama a función de crear tanto de Herbívoro como de Carnívoro, el contador de animales suma 2 en vez de uno.
Citando lo que la profesora contesto en una pregunta anterior a la tuya.
Lo que está pasando es que en C++ la herencia funciona así, un omnivoro está siendo literalmente un hervívoro y un carnívoro a la vez, cuando creas un omnívoro, en realidad por debajo creas un hervívoro y un omnívoro en memoria, por eso cuenta 2.
Hay formas de cambiar este resultado, una sería sobre escribir los conteos, otra, heredar directo de Animal, y probablemente haya otras formas más.
Pero, para fines de la clase, lo que te pasa es correcto.
Crear una clase en C++ que extiende de 2 clases utilizando la herencia múltiple.
Tengo una duda. Si deseo utilizar una clase que tiene por ejemplo (char _nombre, int edad, float calificación);
pero _nombre y _edad ya fueron heredadas de otra clase. esto quiere decir que mi clase "hijo" seria de la forma
class "hijo" : public "padre1" (el que me deseo que me herede solo la calificación ), public "padre2" (el que me hereda _nombre, _edad)
al momento de inicializar mi constructor como podría hacerlo ?
Creo que lo más útil es evitar hacer eso, siempre debemos evitar lo más posible las ambigüedades, en este caso, utilizar diferentes nombres para las variables, así puedes tener cualquiera de los dos datos y elegir cuál usar.
Tengo un problema. Al momento de ejecutar me aparece esto:
¿Qué puedo hacer para solucionarlo?
Hola, ¿ya habías podido correr tus programas en tu entorno?, ¿qué compilador utilizas?, ¿qué extensiones de VScode estás utilizando?
Si, ya había usado ese compilador y me funciona bien. El error me lo marca en la parte de la herencia múltiple, en la parte del omnívoro. Adjunto El error del compilador y mi código:
Cuando creo al Omnivoro, es normal de que cree 2 animales al mismo tiempo? cuando corro el programa me indica de que existen 5 animales :/
Animal*a =newAnimal();Hervivoro*e =newHervivoro();Carnivoro*c =newCarnivoro();Omnivoro*o =newOmnivoro();// genera 2 animales??
Si, cómo menciono en la clase, al heredar de dos clases base, se comporta como una y cómo la otra a la vez, por lo tanto dentro de un omnivoro hay un carnívoro y un hervívoro.
Hay formas de hacer que sea solo un nuevo animal, pero para el ejemplo de clase, lo que te sucede es un comportamiento esperado.
gracias!! como solución se me ocurrió restar -1 a num_animales en el contructor de la clase omnivoro
classOmnivoro:publicHervivoro,publicCarnivoro{// heredamos carnivoro y hervivoropublic:Omnivoro():Hervivoro(),Carnivoro(){ num_animales -=1;}// cuando se inicie el contructor, restamos -1 a num_animales para evitar ambiguedadvoidcomer(void){// método propio de la clase carnivoro cout <<"Los omnivoros pueden comer lo que sea"<< endl;}};
resultado
generando un nuevo animal
generando un nuevo animal
generando un nuevo animal
generando un nuevo animal
generando un nuevo animal
numero de animales:4
alguien ve el error no lo encuentro?
#include<iostream>
#include<stdlib.h>
#include<string.h>
using namespace std;
class animal
{
protected:
static int numero_animales;
string alimento;
public:
animal();
~animal();
static int obtenerNumeroAnimales();
string obtenerAlimento(){
return alimento;
};
void comer(){
cout<<"este animal esta comiendo"<<alimento<<"... ñon ñom"<<endl;
};
};
animal::animal()
{
cout<<"creando nuevo animal ..."<<endl;
numero_animales++;
}
De preferencia compartirlo en un sitio que permita compilar y evaluar online, así está bastante incómodo.
Mi codigo:
#include <iostream>#include <string>using namespace std;//Crear objetoclassAnimal{//private:protected:static int numero_animales; string alimento;public:Animal();~Animal();static int obtenerNumeroAnimales(); string obtenerAlimento(){return alimento;}voidcomer(){ cout<<"Este animal esta comiendo "<<alimento<<". . . yomi yomi"<<endl;}};//Inicializar variableint Animal::numero_animales =0;//Constructor y DestructorAnimal::Animal(){ cout<<"Creando nuevo animal..."<<endl; numero_animales +=1;}Animal::~Animal(){ cout<<"Borrando animal ..."<<endl; numero_animales -=1;}int Animal::obtenerNumeroAnimales(){return numero_animales;}//Clase que HeredaclassHerviboro:publicAnimal{public:Herviboro():Animal(){this->alimento ="plantas";}voidpastar(){ cout<<"Este animal esta pastando ..."<<endl;}};classCarnivoro:publicAnimal{public:Carnivoro():Animal(){this->alimento="carne";}voidcazar(){ cout<<"Este animal esta cazando ..."<<endl;}};classOmnivoro:publicHerviboro,publicCarnivoro{public:Omnivoro():Herviboro(),Carnivoro(){}voidcomer(){ cout<<"Este animal come lo que sea..."<<endl;}};int main(){Animal*a1 =newAnimal();Herviboro*h1 =newHerviboro();Carnivoro*c1 =newCarnivoro();Omnivoro*o1 =newOmnivoro(); cout<<"Numero de animales: "<<Animal::obtenerNumeroAnimales()<<endl; a1->comer(); h1->pastar();//Plantas h1->comer(); c1->cazar();//Carne c1->comer();//? Ominvoro//o1->Herviboro::comer(); //Plantas() de sus Padres//o1->Carnivoro::comer(); //Carne() de sus Padres o1->comer();//En su propio constructordelete a1,h1,c1,o1; cout<<"Numero de animales: "<<Animal::obtenerNumeroAnimales()<<endl;}
A la hora de heredar si se pone un virtual, se soluciona el problema del diamante