Herencia de Clases
La herencia de clases es uno de los conceptos básicos de la programación orientada a objetos. Decir que una clase hereda de otra quiere decir que esa clase obtiene los mismos métodos y propiedades de la otra clase. Permitiendo de esta forma añadir a las características heredadas las suyas propias.
La herencia es, después de la agregación o composición, el mecanismo más utilizado para alcanzar algunos de los objetivos más preciados en el desarrollo de software como lo son la re-utilización y la extensibilidad. A través de ella los diseñadores pueden crear nuevas clases partiendo de una clase o de una jerarquía de clases preexistente evitando con ello el rediseño, la modificación y verificación de la parte ya implementada. La herencia facilita la creación de objetos a partir de otros ya existentes e implica que una subclase obtiene todo el comportamiento y eventualmente los atributos de su superclase.
Es la relación entre una clase general y otra clase más específica. Por ejemplo: Si declaramos una clase párrafo derivada de una clase texto, todos los métodos y variables asociadas con la clase texto, son automáticamente heredados por la subclase párrafo.
Tipos de herencia de clases
Existen dos tipos de herencia:
- Herencia por especialización
- Herencia por generalización
Herencia por especialización
Una herencia por especialización es la que se realiza cuando necesitamos crear una clase nueva que disponga de las mismas características que otra pero que le añada funcionalidades. Por ejemplo, si tenemos una clase que genera un botón simple, y necesitamos crear un botón que sea igual que el anterior pero que además añada un efecto al ser ciclado.
Herencia por generalización
La herencia por generalización es la que realizamos cuando tenemos muchas clases que comparten unas mismas funcionalidades y por homogeneizar las partes comunes se decide crear una clase que implemente toda esa parte común y se dejan solo las partes especificas en cada clase. Por ejemplo, si tenemos clases para dibujar formas geométricas todas ellas
Jerarquía, clases base y clases derivadas
Cada nueva clase obtenida mediante herencia se conoce como clase derivada, y las clases a partir de las cuales se deriva, clases base. Además, cada clase derivada puede usarse como clase base para obtener una nueva clase derivada. Y cada clase derivada puede serlo de una o más clases base. En este último caso hablaremos de derivación múltiple.
Esta propiedad nos permite encapsular diferentes partes de cualquier objeto real o imaginario, y vincularlo con objetos más elaborados del mismo tipo básico, que heredarán todas sus características. Lo veremos mejor con un ejemplo.
-
Derivar clases
La forma general de declarar clases derivadas es la siguiente:
1 2 |
class <clase_derivada> : [public|private] <base1> [,[public|private] <base2>] {}; |
En seguida vemos que para cada clase base podemos definir dos tipos de acceso, public o private. Si no se especifica ninguno de los dos, por defecto se asume que es private.
- public: los miembros heredados de la clase base conservan el tipo de acceso con que fueron declarados en ella.
1 2 3 4 5 6 7 8 |
// Clase base Persona: class Persona { public: Persona(char *LeerNombre, int e); const char *LeerNombre(char *LeerNombre, const); int LeerEdad() const; void CambiarNombre(const char *LeerNombre); void CambiarEdad(int e); |
De momento siempre declararemos las clases base como public, al menos hasta que veamos la utilidad de hacerlo como privadas.
- private: todos los miembros heredados de la clase base pasan a ser miembros privados en la clase derivada.
Veamos un ejemplo sencillo basado en la idea del punto anterior:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
// Clase base Persona: class Persona { public: Persona(char *LeerNombre, int e); const char *LeerNombre(char *LeerNombre, const); int LeerEdad() const; void CambiarNombre(const char *LeerNombre); void CambiarEdad(int e); protected: char nombre[40]; int edad; }; // Clase derivada Empleado: class Empleado { public: Empleado(char *LeerNombre, int e, float s); float LeerSalario() const; void CambiarSalario(const float s); protected: float salarioAnual; }; |
Podrás ver que hemos declarado los datos miembros de nuestras clases como protected. En general es recomendable declarar siempre los datos de nuestras clases como privados, de ese modo no son accesibles desde el exterior de la clase y además, las posibles modificaciones de esos datos, en cuanto a tipo o tamaño, sólo requieren ajustes de los métodos de la propia clase.
Ejemplo de código
Generalmente una clase representa de manera formal un concepto de la realidad. Por ejemplo podríamos tener un objeto llamado jose que es una instancia de la clase obrero. Pero un obrero es a la vez un asalariado, y en un nivel de abstracción mayor un asalariado es una persona.
- persona
- asalariado
- obrero
De la misma manera, podríamos tener otro objeto pedro que es una instancia de la clase medico. Cómo pedro trabaja para un laboratorio de productos famaceúticos, es también un asalariado, pero no un obrero. De esta manera nos queda una relación en el nivel superior de nuestra jerarquía de clases tendríamos que: medico y obrero son asalariados y a su vez personas.
- persona
- asalariado
- obrero
- medico
Si bien podemos seguir la jerarquía de clases hacia arriba diciendo “es un” hay que tener en cuenta que no podemos decir ni que un obrero es un medico, ni que un medico es un obrero, aunque ambos sean asalariado y persona.En el ejemplo siguiente vamos a ver como se podría llegar a armar esta estructura y vemos como se re aprovechan las definiciones de las funciones miembro de las jerarquías superiores cuando no hace falta redefinirlas en las clases de jerarquías más bajas.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 |
#include <iostream> #include <cstring> using namespace std; //definición de la clase Numero class persona { public: persona(char[40] = "", int = 1); void establecerEdad(int); void mostrarEdad(void); void establecerNombre(char[40]); void mostrarNombre(void); private: int edad; char nombre[40]; }; class asalariado : public persona { public: asalariado(char[40] = "" , int = 1, double = 0); void establecerSueldo(double = 0); void mostrarSueldo(void); private: double sueldo; }; class obrero : public asalariado { public: obrero(char[40] = "" , int = 1, double = 0, char[40] = "Funciones Generales"); void establecerCargo(char[40]); void mostrarCargo(void); private: char cargo[40]; }; class medico : public asalariado { public: medico(char[40] = "" , int = 1, double = 0, int = 0); void establecerCantPacientes(int); void mostrarCantPacientes(void); private: int cantpacientes; }; persona::persona(char nom[40], int ed){ establecerEdad(ed); establecerNombre(nom); } void persona::establecerEdad(int ed) {edad = ed;} void persona::mostrarEdad(void){ cout << "Edad: "<< edad << endl;} void persona::establecerNombre(char nom[40]){strcpy (nombre,nom);} void persona::mostrarNombre(void){cout << "Nombre: " << nombre << endl;} asalariado::asalariado(char nom[40] , int ed, double suel){ establecerEdad(ed); establecerNombre(nom); establecerSueldo(suel); } void asalariado::establecerSueldo(double suel){sueldo = suel; } void asalariado::mostrarSueldo(void){ cout << "Sueldo: " << sueldo << endl;} obrero::obrero(char nom[40] , int ed, double suel, char car[40]){ establecerEdad(ed); establecerNombre(nom); establecerSueldo(suel); establecerCargo(car); } void obrero::establecerCargo(char car[40]) {strcpy (cargo,car);} void obrero::mostrarCargo(void) { cout << "Cargo: " << cargo << endl; } medico::medico(char nom[40] , int ed, double suel, int cant){ establecerEdad(ed); establecerNombre(nom); establecerSueldo(suel); establecerCantPacientes(cant); } void medico::establecerCantPacientes(int cant) { cantpacientes = cant; } void medico::mostrarCantPacientes(void) { cout << "Cantidad de pacientes: " << cantpacientes << endl; } //Probamos la clase. int main() { medico pedro("Pedro Gonzales", 40, 5457.45, 15); cout << "********************" << endl; pedro.mostrarNombre(); pedro.mostrarEdad(); pedro.mostrarSueldo(); pedro.mostrarCantPacientes(); cout << "********************" << endl; obrero jose("Jose Alvarez", 54, 1200.32, "Tornero Calificado"); jose.mostrarNombre(); jose.mostrarEdad(); jose.mostrarSueldo(); jose.mostrarCargo(); cout << "********************" << endl; medico juan; juan.mostrarNombre(); juan.mostrarEdad(); juan.mostrarSueldo(); juan.mostrarCantPacientes(); return 0; } |