Bienvenidos sean a este post, hoy veremos otro pilar de OOP.
Esta habilidad es la capacidad que poseen las clases de poder compartir sus propiedades y metodos de una clase madre a sus clases hijas pero no de las hijas a las madres. Es decir, la herencia siempre ira desde arriba hacia abajo y las clases hijas pueden ser madres de otras. Antes de ver un ejemplo vamos a comentar como es su sintaxis:
class nombre_ident : public nombre_madre
{
public:
propiedades
metodos
private:
propiedades
metodos
}
La clase en si no sufrira ningun cambio pero si cuando la hacemos heredera debemos poner los dos puntos seguido de la palabra public y el nombre de la clase madre. Para entender el concepto vamos a analizar un ejemplo y para ello creen un archivo con el nombre de herencia.cpp y le agregaremos el siguiente codigo:
#include <iostream>
class Animal
{
public:
int edad;
void hablar() { std::cout << "Animal hablando" << std::endl; }
private:
};
class Perro: public Animal
{
public:
int peso;
void hablar() { std::cout << "Guau!" << std::endl; }
void setSexo(char s) { sexo = s; }
char getSexo() { return sexo; }
private:
char sexo;
};
class Gato: public Animal
{
public:
int peso;
void hablar() { std::cout << "Miau!!" << std::endl; }
private:
};
int main()
{
Animal generico;
Perro arturo;
Gato invi;
generico.edad = 5;
arturo.edad = 10;
invi.edad = 13;
std::cout << "Animal tiene " << generico.edad << " y dice: ";
generico.hablar();
std::cout << "Arturo tiene " << arturo.edad << " y dice: ";
arturo.hablar();
std::cout << "Invi tiene " << invi.edad << " y dice: ";
invi.hablar();
return 0;
}
En el post anterior vimos como se debe trabajar con las clases pero en este ejemplo, por un tema de practicidad vamos a tenerlos todos en un archivo. Primero definimos a la clase maestra, esta la identificamos como Animal y en ella tendremos una propiedad para la edad y un metodo para indicar cuando esta «hablando». Luego tenemos dos clases que seran herederas de la clase madre. Son similares pero con pequeñas diferencias, comentemos primero a la clase Gato. En ambas clases aplicamos una accion que se denomina override o anulacion. Esta es una capacidad de poder modificar el o los metodos heredados. Esta a diferencia de overload, tiene que ser del mismo tipo, el mismo nombre y los mismos argumentos y solo cambian las instrucciones en el bloque de este. En caso contrario, nos devolvera un error al momento de compilarlo porque no podemos tener dos elementos con el mismo nombre en el mismo scope. Volviendo a la clase Gato tenemos una propiedad llamada peso y luego el metodo hablar donde en lugar de mostrar el mensaje de Animal, lo hace con el propio del animal. Y al igual que en la clase madre tenemos todos los elementos en public.
En el caso de la clase Perro, tenemos una propiedad para peso en la parte publica y otra para el sexo del animal en la parte privada. En la parte publica tambien tenemos varios metodos mas. Entre ellos la anulacion del metodo hablar, donde al igual que en la clase Gato lo usaremos para mostrar el sonido de este animal, y los encargados de establecerr el valor de la propiedad privada y la recuperacion del valor del mismo. Luego tenemos el main, en este primero crearemos tres objetos de cada una de las clases. Luego estableceremos el valor de edad en cada objeto creado. Para finalmente mostrar el valor de edad en cada objeto y el resultado de usar el metodo hablar de cada objeto. Compilemos y veamos como es su salida:
$ ./herencia
Animal tiene 5 y dice: Animal hablando
Arturo tiene 10 y dice: Guau!
Invi tiene 13 y dice: Miau!!
$
Observen como tenemos todos los datos y cada animal se expreso de manera distinta. Vamos a tomar el codigo anterior y vamos a modificarlo de la siguiente manera:
#include <iostream>
class Animal
{
public:
void hablar() { std::cout << "Animal hablando" << std::endl; }
private:
int edad;
};
class Perro: public Animal
{
public:
int peso;
void hablar() { std::cout << "Guau!" << std::endl; }
void setSexo(char s) { sexo = s; }
char getSexo() { return sexo; }
private:
char sexo;
};
class Gato: public Animal
{
public:
int peso;
void hablar() { std::cout << "Miau!!" << std::endl; }
private:
};
int main()
{
Animal generico;
Perro arturo;
generico.edad = 5;
arturo.edad = 10;
std::cout << "Animal tiene " << generico.edad << " y dice: ";
std::cout << "Arturo tiene " << arturo.edad << " y dice: ";
arturo.hablar();
return 0;
}
En este caso, pasamos a private la propiedad edad y eliminamos uno de los objetos pero seguimos creando un objeto de la clase madre y un heredado. Si lo compilamos, sucedera lo siguiente:
$ g++ herencia.cpp -o herencia
herencia.cpp: In member function ‘void Perro::setEdad(int)’:
herencia.cpp:20:31: error: ‘int Animal::edad’ is private within this context
20 | void setEdad(int e) { edad = e; }
| ^~~~
herencia.cpp:10:13: note: declared private here
10 | int edad;
| ^~~~
herencia.cpp: In member function ‘int Perro::getEdad()’:
herencia.cpp:21:32: error: ‘int Animal::edad’ is private within this context
21 | int getEdad() { return edad; }
| ^~~~
herencia.cpp:10:13: note: declared private here
10 | int edad;
| ^~~~
herencia.cpp: In function ‘int main()’:
herencia.cpp:39:18: error: ‘int Animal::edad’ is private within this context
39 | generico.edad = 5;
| ^~~~
herencia.cpp:10:13: note: declared private here
10 | int edad;
| ^~~~
herencia.cpp:40:16: error: ‘int Animal::edad’ is private within this context
40 | arturo.edad = 10;
| ^~~~
herencia.cpp:10:13: note: declared private here
10 | int edad;
| ^~~~
herencia.cpp:42:50: error: ‘int Animal::edad’ is private within this context
42 | std::cout << "Animal tiene " << generico.edad << " y dice: ";
| ^~~~
herencia.cpp:10:13: note: declared private here
10 | int edad;
| ^~~~
herencia.cpp:44:48: error: ‘int Animal::edad’ is private within this context
44 | std::cout << "Arturo tiene " << arturo.edad << " y dice: ";
| ^~~~
herencia.cpp:10:13: note: declared private here
10 | int edad;
| ^~~~
$
Con esta simple accion dejamos de hacer que esta propiedad sea heredada, en realidad puede ser accedida pero tendriamos que crear metodos para poder acceder a la misma o mediante un constructor para establecer un valor al crear un objeto pero no podemos acceder despues. Probemos de tomar la clase Perro y hagamos la siguiente modificacion:
class Perro: public Animal
{
public:
int peso;
void hablar() { std::cout << "Guau!" << std::endl; }
void setSexo(char s) { sexo = s; }
char getSexo() { return sexo; }
void setEdad(int e) { edad = e; }
int getEdad() { return edad; }
private:
char sexo;
};
En este caso agregamos dos metodos para obtener la propiedad edad, y otro para establecerle un valor. Ademas modifiquen a main de la siguiente manera:
int main()
{
Perro arturo;
arturo.setEdad(10);
std::cout << "Arturo tiene " << arturo.getEdad() << " y dice: ";
arturo.hablar();
return 0;
}
En este caso solo dejamos al objeto de la clase Perro, donde mediante setEdad le establecemos un valor y recuperamos el valor mediante getEdad, compilemos y veamos que sucede:
$ g++ herencia.cpp -o herencia
herencia.cpp: In member function ‘void Perro::setEdad(int)’:
herencia.cpp:18:31: error: ‘int Animal::edad’ is private within this context
18 | void setEdad(int e) { edad = e; }
| ^~~~
herencia.cpp:8:13: note: declared private here
8 | int edad;
| ^~~~
herencia.cpp: In member function ‘int Perro::getEdad()’:
herencia.cpp:19:32: error: ‘int Animal::edad’ is private within this context
19 | int getEdad() { return edad; }
| ^~~~
herencia.cpp:8:13: note: declared private here
8 | int edad;
| ^~~~
$
Vuelve a fallar porque como mencionamos, al ser private niega completamente el acceso. Pero aqui entra en accion un nuevo modificador, este lleva el nombre de protected. Este tiene la particularidad de que los elementos en el seran de tipo public si los accedemos desde la clase misma o una heredera de esta, y sera private para las instancias u objetos creados desde la clase propia o herederas. Tomemos la clase Animal y hagamos el siguiente cambio:
class Animal
{
public:
void hablar() { std::cout << "Animal hablando" << std::endl; }
protected:
int edad;
};
Simplemente reemplazamos a private por protected y no modificamos nada del codigo anterior. Compilemos y veamos como es la salida:
$ ./herencia
Arturo tiene 10 y dice: Guau!
$
Como pueden ver funciono perfectamente, volvamos al archivo pero modifiquemos el codigo de main de la siguiente manera:
int main()
{
Animal generico;
Perro arturo;
generico.edad = 5;
arturo.setEdad(10);
std::cout << "Animal tiene " << generico.edad << " y dice: ";
generico.hablar();
std::cout << "Arturo tiene " << arturo.getEdad() << " y dice: ";
arturo.hablar();
return 0;
}
En este caso, volvemos a crear un objeto de la clase Animal, le establecemos un valor para la edad e intentamos mostrarlo en pantalla, asi como tambien un llamado a hablar. Compilemos y veamos como es la salida:
$ g++ herencia.cpp -o herencia
herencia.cpp: In function ‘int main()’:
herencia.cpp:37:18: error: ‘int Animal::edad’ is protected within this context
37 | generico.edad = 5;
| ^~~~
herencia.cpp:8:13: note: declared protected here
8 | int edad;
| ^~~~
herencia.cpp:40:50: error: ‘int Animal::edad’ is protected within this context
40 | std::cout << "Animal tiene " << generico.edad << " y dice: ";
| ^~~~
herencia.cpp:8:13: note: declared protected here
8 | int edad;
| ^~~~
$
Vuelve a fallar por lo comentado anteriormente, este desde cualquier instancia, tanto directa como heredada, se considera como privada por lo tanto necesitamos de un metodo publico para poder acceder. Pero este al brindarnos la posibilidad de ser publica para las clases hijas o herederas, podemos crear los metodos en esta para poder acceder a los mismos. Es decir, se puede hacer en la clase base o madre pero tambien lo podemos hacer en las hijas pero no podremos accederlos desde las instancias u objetos. Por esta razon, veran que el protected es muy utilizado especialmente para las herencias entre clases.
Comentamos que tenemos una tecnica similar a sobrecarga pero que anula los metodos heredados para ser propios de cada clase y manteniendo el mismo nombre. Pero esto no quita que podemos utilizar a sobrecarga, tomemos como ejemplo a la clase Perro y hagamos los siguientes cambios:
class Perro: public Animal
{
public:
int peso;
Perro(){}
Perro(int e, int p) { edad = e; peso = p; }
Perro(int e, int p, char s) {
edad = e;
peso = p;
sexo = s;
}
void hablar() { std::cout << "Guau!" << std::endl; }
void setSexo(char s) { sexo = s; }
char getSexo() { return sexo; }
void setEdad(int e) { edad = e; }
int getEdad() { return edad; }
private:
char sexo;
};
La modificacion que implementamos es una sobrecarga del constructor de la clase. La primera es la predeterminada y cuando realizamos una sobrecarga de estos debemos agregarla. La siguiente sobrecarga recibe dos valores y estos los usaremos para iniciar a la propiedad heredada (edad) y a peso. La ultima sobrecarga nos da la posibilidad de poder iniciar a las tres propiedades. Observen que podemos trabajar con edad porque esta en protected y para esta clase es como si fuera publica. El resto sigue de la misma manera. Nuestro siguiente paso sera modificar a main de la siguiente manera:
int main()
{
Perro arturo(10, 35);
Perro lola(7, 25, 'F');
std::cout << "Arturo tiene " << arturo.getEdad() << " y dice: ";
arturo.hablar();
std::cout << "Lola tiene " << lola.getEdad() << " pesa ";
std::cout << lola.peso << " y es ";
std::cout << (lola.getSexo()=='M' ? "Perro" : "Perra") << std::endl;
return 0;
}
Primero crearemos dos objetos de la clase Perro y en cada uno usaremos un constructor distinto. En el primer caso, mostraremos la edad de este y usaremos al metodo hablar, tal como vinimos haciendo hasta ahora, y para el segundo objeto tambien mostraremos la edad mediante su metodo, luego el valor de la propiedad peso y por ultimo mostraremos el valor de sexo pero no lo mostraremos directamente sino que usamos al operador condicional para verificar el valor que le pasamos y dependiendo de este mostrara si es un perro o una perra. Con esto comentado, pasemos a compilarlo y ver como es su salida:
$ ./herencia
Arturo tiene 10 y dice: Guau!
Lola tiene 7 pesa 25 y es Perra
$
Observen como no solo mostro los datos que le pasamos sino que a su vez pudo manipularlos tranquilamente y pudimos crear dos objetos con distintas formas de iniciarlos. Hasta aqui hemos cubierto la base de lo que es la herencia en las clases pero nos quedan algunos aspectos que cubriremos en el proximo post.
En resumen, hoy hemos visto como es la herencia, como se aplica, que nos permite, algunas caracteristicas de la misma, asi como tambien como podemos aplicar otros temas vistos anteriormente. Espero les haya resultado de utilidad 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.


Donatión
It’s for site maintenance, thanks!
$1.50
