Bienvenidos a mi post, hoy tocaremos el tema de la herencia y derivacion entre clases, C++ tiene una posibilidad de que una clase herede metodos de una clase superior. La forma de declarar una herencia es de la siguiente forma:
class Clase2 : public Clase1 { ... instrucciones ... }
Para poder realizar una clase derivada tenemos que declarar primeramente una funcion maestra, en este caso llamada Clase1 con sus respectivos datos y funciones miembros, luego creamos una clase Clase2 que a traves de los dos puntos (:) se denomina que hereda los metodos de la clase Clase1 y a su vez de forma publica, para poder efectuar esto se debe crear una «jerarquia» dentro del programa, esto significa que en el caso de las clases derivadas siempre va a haber una clase base, denominada Clase1, y en Clase2 vamos a tener acceso a todos los metodos y variables miembros de la Clase1, pasemos al siguiente ejemplo para entender la herencia entre clases:
herencia00.cpp
# include <iostream>
using namespace std;
enum RAZA { GOLDEN, CAIRN, DANDIE, SHETLAND, DOBERMAN, LAB };
class Mamifero
{
public:
Mamifero(): suEdad(2), suPeso(5) {}
~Mamifero(){}
int ObtenerEdad() const { return suEdad; }
void AsignarEdad(int edad) { suEdad = edad; }
int ObtenerPeso() const { return suPeso; }
void AsignarPeso(int peso) { suPeso = peso; }
void Hablar() const { cout << "Sonido de Mamifero!!!!\n"; }
void Dormir() const { cout << "Shhh. Estoy Durmiendo...\n"; }
protected:
int suEdad;
int suPeso;
};
class Perro : public Mamifero
{
public:
Perro(): suRaza(GOLDEN) {}
~Perro(){}
RAZA ObtenerRaza() const { return suRaza; }
void AsignarRaza(RAZA raza) { suRaza = raza; }
void MoverCola() const { cout << "Moviendo la cola... \n"; }
void PedirAlimento() const { cout << "Pidiendo Alimento...\n"; }
protected:
RAZA suRaza;
};
int main()
{
Perro fido;
fido.Hablar();
fido.MoverCola();
cout << "fido tiene ";
cout << fido.ObtenerEdad() << " años de edad\n";
return 0;
}
Analicemos el ejemplo, noten que en este caso cuando definimos las clases no utilizamos private sino protected, esta es una nueva opcion que podemos utilizar en nuestras clases. Entonces, porque protected y no private? Es debido a que si hubieramos utilizado el private, no hubieramos podido tener acceso a la informacion declarada en la clase base a traves de clases derivadas, tambien lo podriamos haber hecho public pero nosotros no queremos que esta informacion puede ser accedida por cualquiera parte del programa, en cambio cuando la definimos como protected esta es publica solamente para las clases derivadas de la clase base y esta protegida del resto del programa. De ahora en adelante, a nuestras clases podemos definirlas como tres tipos de informacion, public, private y protected. La clase maestra, en este caso tenemos a Mamifero, nos definira los datos miembro y metodos que van a ser heredados por el resto de las clases y son comunes para los demas, como puede ser suEdad, suPeso, Hablar y Dormir que se pueden suponer que son los metodos basicos para los mamiferos. Ahora creamos una clase llamada Perro y esta se le indica que herede todos los miembro de la clase Mamifero pero a su vez va a tener sus propios datos y metodos, en este caso suRaza, MoverCola y PedirAlimento. Ahora pasemos a analizar el main, creamos un objeto de Perro llamado fido, a este objeto le decimos que ejecute el metodo Hablar (que viene heredado de Mamifero), MoverCola que es el metodo propio de la clase Perro. Y despues le decimos que utilicemos ObtenerEdad para conseguir el valor predeterminado de suEdad que esta definido en la clase Maestra. La salida del programa es esta:
tinchicus@dbn001dsk:~/lenguaje/c++$ ./herencia00 Sonido de Mamifero!!!! Moviendo la cola… fido tiene 2 años de edad tinchicus@dbn001dsk:~/lenguaje/c++$
Los constructores y destructores, como en toda las clases debe tener ambos, aunque sean derivadas tanto en Mamifero como en Perro debemos declararlas y definirlas, gracias a esto podremos crear a fido. Despues debemos usar los respectivos destructores para eliminar las partes de fido que se pudieron crear con el programa. Veamos los partes correspondientes:
El constructor y destructor de Mamifero:
Mamifero(): suEdad(2), suPeso(5) {}
~Mamifero(){}
El constructor y destructor de Perro:
Perro(): suRaza(GOLDEN) {}
~Perro(){}
Supongamos que necesitamos pasar algunos argumentos para nuestro Perro como puede ser su edad, su peso o su raza. En estos casos deberiamos hacer como hicimos con las sobrecargas de constructores, en donde definiamos uno predeterminado (sin valores) y el resto con distintos tipos de variables, asi por ejemplo nosotros podriamos en la clase Mamifero generar estos dos constructores:
Mamifero();
Mamifero(int edad);
Y en la clase Perro, no solamente queremos que se defina su edad, sino tambien su peso y su raza, entonces podriamos sobrecargarlo de esta forma:
Perro();
Perro(int edad);
Perro(int edad, int peso);
Perro(int edad, RAZA raza);
Perro(int edad, int peso, RAZA raza);
Donde cada constructor iniciara sus respectivos valores que nos permitira modificar los datos iniciales dependiendo del metodo que querramos usar. Para verlo necesitamos de un ejemplo muy complejo que lo subire en este link, pasemos a otro tema interesante, la redefinicion de funciones. Como vimos hasta ahora las clases derivadas (o heredadas) no solamente utilizan los metodos de su propia clase sino ademas los de la clase base, ahora si nosotros en la clase derivada generamos exactamente la misma funcion que en la clase base a esto se lo denomina redefinicion de funcion, permitiendo que la funcion que se declara originalmente sea reemplazada unicamente para la clase donde se redefinio. Veamos el siguiente ejemplo:
herencia01.cpp
# include <iostream>
using namespace std;
enum RAZA { GOLDEN, CAIRN, DANDIE, SHETLAND, DOBERMAN, LAB };
class Mamifero
{
public:
Mamifero(){}
~Mamifero(){}
void Hablar() const { cout << "Sonido de mamifero\n"; }
void Dormir() const { cout << "Shhh. Estoy durmiendo\n"; }
protected:
int suEdad;
int suPeso;
};
class Perro : public Mamifero
{
public:
Perro(){}
~Perro(){}
void Hablar() const { cout << "Guau!\n"; }
void MoverCola() const { cout << "Moviendo la cola...\n"; }
void PedirAlimento() const { cout << "Pidiendo alimento...\n"; }
private:
RAZA suRaza;
};
int main()
{
Mamifero animalGrande;
Perro fido;
cout << "Animal Grande: ";
animalGrande.Hablar();
cout << "Fido: ";
fido.Hablar();
cout << "Animal Grande: ";
animalGrande.Dormir();
cout << "Fido: ";
fido.Dormir();
return 0;
}
En este ejemplo tenemos dos metodos definidos en Mamifero, Hablar y Dormir. y en la siguiente clase tenemos tres metodos: Hablar, MoverCola y PedirAlimento. La funcion Hablar fue redefinida en vez de decir «Sonido de mamifero» ahora nos va a devolver una respuesta mas apropiada a la clase (Perro) esta va a decir «Guau«, pasemos al main, en este se crean dos objetos uno animalGrande de la clase Mamifero y otro fido de la clase Perro, ambos objetos llaman a Hablar y Dormir, de la clase Mamifero pero observen la salida del programa:
tinchicus@dbn001dsk:~/lenguaje/c++$ ./herencia01 Animal Grande: Sonido de mamifero Fido: Guau! Animal Grande: Shhh. Estoy durmiendo Fido: Shhh. Estoy durmiendo tinchicus@dbn001dsk:~/lenguaje/c++$
animalGrande va a utilizar los metodos definidos en su clase pero en cambio fido va a utilizar la funcion que se redefinio en su clase pero si va a seguir utilizando el Dormir de la clase heredada. Como ven nos permitio definir algo mas aproximado a lo que realmente diria un perro en vez de lo que esta definido en la funcion de la clase base, tambien como se ve esto solamente afecta a los objetos creados en la clase Perro, para otro tipo de clases que sean herederas de Mamifero usaran el Hablar de la clase base (Mamifero), siempre y cuando no se vuelva a redefinir en las mismas. Ahora, suponiendo que tenemos una sobrecarga de funciones en la clase base, si nosotros redefinimos la funcion, esto nos anulara automaticamente la sobrecarga de las funciones base. Esto puede crear problemas de compilacion dado que al efectuar esta accion, el compilador encontrara esta incongruencia y no nos permitira compilarlo. Este problema habitualmente se lo llama ocultamiento de metodo de clase base, una solucion es volver a redefinir todos los metodos sobrecargados y sino es llamarlo especificando el nombre completo del metodo oculto. Veamos un ejemplo:
herencia02.cpp
# include <iostream>
using namespace std;
class Mamifero
{
public:
void Mover() const { cout << "Mamifero se mueve un paso.\n"; }
void Mover(int distancia) const
{
cout << "Mamifero se mueve " << distancia << " pasos.\n";
}
protected:
int suEdad;
int suPeso;
};
class Perro:public Mamifero
{
public:
void Mover() const { cout << "Perro se mueve 5 pasos.\n"; }
};
int main()
{
Mamifero animalGrande;
Perro fido;
animalGrande.Mover();
animalGrande.Mover(2);
fido.Mover();
fido.Mamifero::Mover(10);
return 0;
}
En este caso tenemos una sobrecarga de la funcion Mover en la clase base, y nosotros la redefinimos en Perro, esto hara que se modifique la primera funcion de Mover y ocultara la que esta debajo y en el caso de que la llamaramos nos produciria un error, para evitar justamente este inconveniente utilizamos:
fido.Mamifero::Mover(10);
Vemos que para evitar que ocurra un error de compilacion hacemos que fido llame la ruta completa de donde esta el metodo oculto, es decir la clase seguido por dos veces los dos puntos (:) y el nombre del metodo. Esto evitara que el compilador genere un error y se aborte. Primero vamos a compilarlo y ver cual es la salida:
tinchicus@dbn001dsk:~/lenguaje/c++$ ./herencia02 Mamifero se mueve un paso. Mamifero se mueve 2 pasos. Perro se mueve 5 pasos. Mamifero se mueve 10 pasos. tinchicus@dbn001dsk:~/lenguaje/c++$
Si modificamos la linea: fido.Mamifero::Mover(10); por: fido.Mover(10); nos devolvera el siguiente error al compilarlo:
tinchicus@dbn001dsk:~/lenguaje/c++$ g++ herencia02.cpp -o herencia02 herencia02.cpp: In function ‘int main()’: herencia02.cpp:31:16: error: no matching function for call to ‘Perro::Mover(int ’ fido.Mover(10); ^ herencia02.cpp:21:8: note: candidate: void Perro::Mover() const void Mover() const { cout << "Perro se mueve 5 pasos.\n"; } ^~~~~ herencia02.cpp:21:8: note: candidate expects 0 arguments, 1 provided tinchicus@dbn001dsk:~/lenguaje/c++$
El compilador nos dice donde esta la falla, el tipo de error y en algunas ocasiones una descripcion de la falla pero no siempre es asi. Hasta ahora hemos visto que una clase puede heredar los datos y metodos de una clase base, tambien se permite la posibilidad de que apuntadores de la clase base se asignen a las clases derivadas, se podria generar la siguiente linea:
Mamifero * apMamifero = new Perro;
Este tipo de procedimiento es llamado metodo virtual, veamos un ejemplo para luego ahondar mas en detalles:
virtual.cpp
# include <iostream>
using namespace std;
class Mamifero
{
public:
Mamifero(){}
virtual ~Mamifero(){}
virtual void Hablar() const { cout << "Mamifero Habla!!!\n"; }
protected:
int suEdad;
};
class Perro : public Mamifero
{
public:
void Hablar() const { cout << "Guau!\n"; }
};
class Gato : public Mamifero
{
public:
void Hablar() const { cout << "Miau!!\n"; }
};
class Caballo : public Mamifero
{
public:
void Hablar() const { cout << "Yihii!!!\n"; }
};
class Cerdo : public Mamifero
{
public:
void Hablar() const { cout << "Oink!!\n"; }
};
int main()
{
Mamifero * elArreglo[ 5 ];
Mamifero * aptr;
int opcion, i;
for(i=0; i<5; i++)
{
cout << "(1)perro (2)gato (3)caballo (4)cerdo: ";
cin >> opcion;
switch(opcion)
{
case 1:
aptr = new Perro;
break;
case 2:
aptr = new Gato;
break;
case 3:
aptr = new Caballo;
break;
case 4:
aptr = new Cerdo;
break;
default:
aptr = new Mamifero;
break;
}
elArreglo[ i ]=aptr;
}
for(i=0;i<5;i++)
{
elArreglo[i] -> Hablar();
}
return 0;
}
En este caso, aparece una nueva clave esta se llama virtual, esta clave indica que eventualmente esta clase se convertira en la base de otra clase, y seguramente debamos redefinir al metodo en las clases derivadas, por ejemplo en este caso definimos que Hablar sea la virtual de la clase base (Mamifero), despues en el resto de las clases nos encargamos de volver a redefinirlas. Esto nos permitira el llamado correcto del metodo Hablar de cada clase, ahora en main definimos dos apuntadores para Mamifero, uno va a ser un Array llamado elArreglo y va a guardar cinco datos y otro apuntador mas llamado aptr, tambien dos variables enteras opcion e i. Pasemos a ver las acciones, observen que tenemos un comando FOR que nos hara un bucle de 5 preguntas, en esta debemos ingresar un numero de entre uno y cinco, como se nos indica en pantalla, este bucle tiene un switch, este se encargara en base al numero que ingresamos de compararlo con los case y dependiendo de cada caso este creara un nuevo apuntador para cada una de las clases, en caso de que el numero sea mayor que cuatro utilizara el valor por default. Despues de pasado el switch, la posicion del array estara asignada por el valor de i, que es el contador del for, y a este se le asigna el apuntador que decidio el switch. Una vez que tenemos cargado el array, utilizamos un for que nos permite llamar al metodo Hablar para cada una de la clases derivadas. Este ejecutara los metodos de cada una clases que nosotros fuimos asignando. Les muestro la salida del programa:
tinchicus@dbn001dsk:~/lenguaje/c++$ ./virtual (1)perro (2)gato (3)caballo (4)cerdo: 4 (1)perro (2)gato (3)caballo (4)cerdo: 5 (1)perro (2)gato (3)caballo (4)cerdo: 1 (1)perro (2)gato (3)caballo (4)cerdo: 3 (1)perro (2)gato (3)caballo (4)cerdo: 2 Oink!! Mamifero Habla!!! Guau! Yihii!!! Miau!! tinchicus@dbn001dsk:~/lenguaje/c++$
En la salida se ve lo que dijimos antes, a medida que completan la eleccion esta va guardando en una posicion del array (elArreglo) el apuntador de la clase que se eligio. Ahora cuando termina el bucle, va a buscar al array cada uno de los apuntadores guardados y este a su vez invoca al metodo Hablar que corresponde a cada una dando como resultado lo que se ve arriba, es bastante simple. Esto que vimos, es habitualmente llamada magia de la funcion virtual y tengan en cuenta que solo funciona con apuntadores y referencias, si se pasa un objeto por valor no se podra invocar sus funciones. Para ver mejor los metodos virtuales y particiones de datos pasemos al siguiente ejemplo:
virtual00.cpp
# include <iostream>
using namespace std;
class Mamifero
{
public:
Mamifero(): suEdad(1) {}
virtual ~Mamifero(){}
virtual void Hablar() const { cout << "Mamifero Habla\n"; }
protected:
int suEdad;
};
class Perro:public Mamifero
{
public:
void Hablar() const { cout << "Guau!\n"; }
};
class Gato:public Mamifero
{
public:
void Hablar() const { cout << "Miau!!\n"; }
};
void ValorFuncion(Mamifero);
void AptrFuncion(Mamifero *);
void RefFuncion(Mamifero &);
int main()
{
Mamifero * aptr = NULL;
int opcion;
while(1)
{
bool fSalir = false;
cout << "(1)Perro (2)Gato (0)Salir: ";
cin >> opcion;
switch(opcion)
{
case 0:
fSalir = true;
break;
case 1:
aptr = new Perro;
break;
case 2:
aptr = new Gato;
break;
default:
aptr = new Mamifero;
break;
}
if (fSalir)
break;
AptrFuncion(aptr);
RefFuncion(*aptr);
ValorFuncion(*aptr);
}
return 0;
}
void ValorFuncion(Mamifero MamiferoValor)
{
MamiferoValor.Hablar();
}
void AptrFuncion(Mamifero * apMamifero)
{
apMamifero -> Hablar();
}
void RefFuncion(Mamifero & rMamifero)
{
rMamifero.Hablar();
}
En este programa vemos una version resumida de tres clases Mamifero, Perro y Gato, despues declaramos tres funciones, ValorFuncion, AptrFuncion y RefFuncion, y estos toman un objeto, un apuntador y una referencia respectivamente de Mamifero, y las tres funciones llaman al metodo Hablar. Despues se le pide al usuario que elija entre perro y gato y con base a esto, se crea el apuntador al tipo correcto les muestro la salida:
tinchicus@dbn001dsk:~/lenguaje/c++$ ./virtual00 (1)Perro (2)Gato (0)Salir: 3 Mamifero Habla Mamifero Habla Mamifero Habla (1)Perro (2)Gato (0)Salir: 1 Guau! Guau! Mamifero Habla (1)Perro (2)Gato (0)Salir: 2 Miau!! Miau!! Mamifero Habla (1)Perro (2)Gato (0)Salir: 3 Mamifero Habla Mamifero Habla Mamifero Habla (1)Perro (2)Gato (0)Salir: 0 tinchicus@dbn001dsk:~/lenguaje/c++$
En la salida pueden observar varias cosas, si ponen uno este crea un apuntador del objeto Perro. Una vez elegido este se pasa como apuntador, referencia y valor a las tres funciones. En este caso el apuntador y la referencia invocan a las funciones virtuales, y se utiliza la funcion:
Perro -> Hablar();
Esto se ve reflejado en las dos primeras lineas. En la tercer linea se ve como el apuntador desreferenciado se pasa por valor. La funcion espera recibir un Objeto Mamifero, por lo que el compilador parte el objeto Perro dejando solo la parte Mamifero. En ese momento, se hace una llamada al metodo Hablar() de Mamifero, como se refleja en la tercera linea de salida despues de la eleccion del usuario. Si eligen gato, pasa exactamente lo mismo que se describio anteriormente igualmente pueden verlo reflejado en la salida mostrada. Si lo notaron, desde que empezamos con los metodos virtuales, al destructor siempre se le agrego tambien la palabra virtual, esto es llamado destructor de la clase derivada. Lo que hace basicamente es invocar al destructor de la clase base, permitiendo todo el objeto sea destruido de manera apropiada. Ahora hablemos un tema importante, no se puede crear metodos virtuales para los constructores de una clase, pero si podemos crear un metodo en la clase base y hacerlo virtual, este metodo puede crear una copia del nuevo objeto de la clase actual y regresa ese objeto. Veamoslo con el siguiente ejemplo:
virtual01.cpp
# include <iostream>
using namespace std;
enum ANIMALES { MAMIFERO, PERRO, GATO };
const int NumTiposAnimales = 3;
class Mamifero
{
public:
Mamifero(): suEdad(1){
cout << "Constructor de Mamifero...\n";
}
virtual ~Mamifero(){
cout << "Destructor de Mamifero...\n";
}
Mamifero(const Mamifero & rhs);
virtual void Hablar() const { cout << "Mamifero Habla\n"; }
virtual Mamifero * Clonar(){
return new Mamifero(*this);
}
int ObtenerEdad() const { return suEdad; }
protected:
int suEdad;
};
Mamifero::Mamifero(const Mamifero & rhs):
suEdad(rhs.ObtenerEdad())
{
cout << "Constructor de copia de Mamifero...\n";
}
class Perro : public Mamifero
{
public:
Perro(){
cout << "Contructor de Perro...\n";
}
virtual ~Perro(){
cout << "Destructor de Perro...\n";
}
Perro(const Perro & rhs);
void Hablar() const { cout << "Guau!\n"; }
virtual Mamifero * Clonar(){
return new Perro(*this);
}
};
Perro::Perro(const Perro & rhs):
Mamifero(rhs){
cout << "Constructor de copia de Perro...\n";
}
class Gato : public Mamifero
{
public:
Gato(){
cout << "Constructor de Gato...\n";
}
virtual ~Gato(){
cout << "Destructor de Gato...\n";
}
Gato(const Gato &);
void Hablar() const { cout << "Miau!!\n"; }
virtual Mamifero * Clonar(){
return new Gato(*this);
}
};
Gato::Gato(const Gato & rhs):
Mamifero(rhs){
cout << "Constructor de copia de Gato...\n";
}
int main()
{
Mamifero * elArreglo[ NumTiposAnimales ];
Mamifero * aptr;
int opcion, i;
for(i=0; i < NumTiposAnimales; i++)
{
cout << "(1)Perro (2)Gato (3)Mamifero: ";
cin >> opcion;
switch(opcion)
{
case 1:
aptr = new Perro;
break;
case 2:
aptr = new Gato;
break;
default:
aptr = new Mamifero;
break;
}
elArreglo[ i ] = aptr;
}
Mamifero * OtroArreglo[ NumTiposAnimales ];
for(i = 0; i < NumTiposAnimales; i++)
{
elArreglo[ i ]-> Hablar();
OtroArreglo[ i ] = elArreglo[ i ] -> Clonar();
}
for(i=0; i < NumTiposAnimales; i++)
{
OtroArreglo[ i ] -> Hablar();
}
return 0;
}
En este ejemplo utilizaremos el metodo Clonar() para hacer la copia «virtual» entre los constructores, en cada una de las clases vamos a crear un metodo virtual llamado Clonar(). Este devuelve un apuntador de un nuevo objeto Mamifero y copiandose a si misma a traves del apuntador this como referencia const, Como cada clase tiene su propio metodo de Clonar, inicializara sus propios constructores de copia con sus respectivos datos, y al estar declarados como virtuales, se creara un pseudo constructor de copia virtual. En el programa, se le solicita al usuario que ingrese el tipo de animal (Perro, Gato, Mamifero) que vamos a utilizar, se hace a traves de un ciclo de tres preguntas, dependiendo del valor que ingresemos se creara un nuevo apuntador por medio del switch y este lo guardara dentro de elArreglo (array), una vez almacenada toda la informacion procedemos a crear otro nuevo array llamado OtroArreglo, y su tamaño queda delimitado por la variable NumTiposAnimales que tiene un valor de tres, en esta vamos a hacer la copia de constructores virtuales, por medio de la llamada Clonar(). En este ciclo tambien definido por NumTiposAnimales, vamos a ejecutar el metodo Hablar de cada apuntador asignado dentro de elArreglo y hace su vez vamos a «clonarlo» en otro array(OtroArreglo), terminado el ciclo, procedemos con otro ciclo para verificar que efectivamente se realizo la copia de los constructores con todos los datos pertinentes, si compilamos el programa la salida del programa es asi:
tinchicus@dbn001dsk:~/lenguaje/c++$ ./virtual01 (1)Perro (2)Gato (3)Mamifero: 3 Constructor de Mamifero… (1)Perro (2)Gato (3)Mamifero: 1 Constructor de Mamifero… Contructor de Perro… (1)Perro (2)Gato (3)Mamifero: 2 Constructor de Mamifero… Constructor de Gato… Mamifero Habla Constructor de copia de Mamifero… Guau! Constructor de copia de Mamifero… Constructor de copia de Perro… Miau!! Constructor de copia de Mamifero… Constructor de copia de Gato… Mamifero Habla Guau! Miau!! tinchicus@dbn001dsk:~/lenguaje/c++$
A medida que vamos contestando se van creando los constructores correspondientes, una vez finalizado el cuestionario vemos que ejecuta el metodo Hablar correspondiente a cada apuntador creado y luego genera los constructores de copia virtual, y una vez finalizado procede con la verificacion de las copias en las ultimas tres lineas. Algo que no comente hasta ahora, cuando se crea metodos virtuales, el compilador genera una tabla de funciones virtuales llamada habitualmente tabla v, se utilizan para mantener cada tipo y objeto de ese tipo, tambien mantiene un apuntador de tabla virtual el cual apunta a esa tabla, esto significa que con la utilizacion de metodos virtuales tambien vamos a tener un mayor incremento en el uso de memoria, por esto como estuvimos diciendo hasta ahora queda completamente a criterio del programador si necesita utilizar metodos virtuales o no, en general para clases pequeñas de las cuales no se espera derivar otras clases es recomendable no utilizarlas, y recuerden que al utilizar los metodos virtuales lo recomendado que todos los destructores (especialmente los de la clase base) sean virtuales para no dejar informacion errante por el programa y por ultimo, recuerden que los constructores nunca van como virtuales.
En resumen, hoy hemos visto hablado sobre herencia, esta es una de las columnas fundamentales de este lenguaje, como se pueden usar referencias y apuntadores, sobrecarga de funciones entre las heredadas, metodos virtuales para nuestras clases herederas, espero les haya sido util sigueme en tumblr, Twitter o Facebook para recibir una notificacion cada vez que subo un nuevo post en este blog, nos vemos en el proximo post.
Tengo un Patreon donde podes acceder de manera exclusiva a material para este blog antes de ser publicado, sigue los pasos del link para saber como.


Tambien podes donar
Es para mantenimiento del sitio, gracias!
$1.50