Herencia La herencia es el proceso por el cual un objeto puede adquirir las propiedades de otro objeto. En C++, la herencia se soporta permitiendo a una clase incorporar otra clase dentro de su declaración. Las clases que heredan propiedades se llaman clase derivada, mientras que la clase de la que se heredan se denomina clase base. La forma de declarar una clase derivada es: class <nombre_clase_derivada> : <acceso> <nombre_clase_base> <acceso> puede ser public o private: - public: los miembros public siguen siendo public, los private en private y los protected en protected. -private: los miembros public, private y protected son todos private en la clase derivada. - protected: los miembros public y protected pasan a ser protected y los private siguen siendo private. MTP II C++ Herencia // Clase base Persona: class Persona { public: Persona(char *n, int e); const char *LeerNombre(char *n) const; int LeerEdad() const; void CambiarNombre(const char *n); void CambiarEdad(int e); protected: char nombre[40]; int edad; }; // Clase derivada Empleado: class Empleado : public Persona { public: Empleado(char *n, int e, float s); float LeerSalario() const; void CambiarSalario(const float s); protected: float salarioAnual; }; MTP II C++ Herencia Constructores de las clases derivadas: Cuando se crea un objeto de una clase derivada, primero se invoca al constructor de la clase o clases base y a continuación al constructor de la clase derivada. Si la clase base es a su vez una clase derivada, el proceso se repite recursivamente. class ClaseA { public: ClaseA() : datoA(10) {cout << "Constructor de A" << endl;} int LeerA() const {return datoA;} protected: int datoA; }; class ClaseB : public ClaseA { public: ClaseB() : datoB(20) {cout << "Constructor de B" << endl;} int LeerB() const {return datoB;} protected: int datoB; }; MTP II C++ Herencia int main() { ClaseB objeto; cout << "a = " << objeto.LeerA() << ", b = " << objeto.LeerB() << endl; system("pause"); return 0; } La salida es ésta: Constructor de A Constructor de B a = 10, b = 20 Presione una tecla para continuar . . . Se ve claramente que primero se llama al constructor de la clase base A, y después al de la clase derivada B. MTP II C++ Herencia Cuando queramos inicializar las clases base usando parámetros desde el constructor de una clase derivada lo haremos de modo análogo a como lo hacemos con los datos miembro, usaremos el constructor de la clase base con los parámetros adecuados. Las llamadas a los constructores deben escribirse antes de las inicializaciones de los parámetros. <clase_derivada>(<lista_de_parámetros>) : <clase_base>(<lista_de_parámetros>) {} class ClaseA { public: ClaseA(int a) : datoA(a) {cout << "Constructor de A" << endl;} int LeerA() const {return datoA;} protected: int datoA; }; class ClaseB : public ClaseA { public: ClaseB(int a, int b) : ClaseA(a), datoB(b) {cout << "Constructor de B" << endl;} (1) int LeerB() const {return datoB;} protected: int datoB; }; MTP II C++ int main() Herencia { ClaseB objeto(5,15); cout << "a = " << objeto.LeerA() << ", b = " << objeto.LeerB() << endl; system("pause"); return 0; } La salida es ésta: Constructor de A Constructor de B a = 5, b = 15 Presione una tecla para continuar . . . Observar cómo hemos definido el constructor de la ClaseB (1). Para empezar, recibe dos parámetros: "a" y "b". El primero se usará para inicializar la clase base ClaseA, para ello, después de los dos puntos, escribimos el constructor de la ClaseA, y usamos como parámetro el valor "a". A continuación escribimos la inicialización del datoB, separado con una coma y usamos el valor "b". MTP II C++ Herencia Destructores de las clases derivadas: Cuando se destruye un objeto de una clase derivada, primero se invoca al destructor de la clase derivada, si existen objetos miembro a continuación se invoca a sus destructores y finalmente al destructor de la clase o clases base. Si la clase base es a su vez una clase derivada, el proceso se repite recursivamente. class ClaseA { public: ClaseA() : datoA(10) {cout << "Constructor de A" << endl;} ~ClaseA() {cout << "Destructor de A" << endl;} int LeerA() const {return datoA;} protected: int datoA; }; class ClaseB : public ClaseA { public: ClaseB() : datoB(20) {cout << "Constructor de B" << endl;} ~ClaseB() {cout << "Destructor de B" << endl;} int LeerB() const {return datoB;} protected: int datoB; }; MTP II C++ int main() Herencia { ClaseB objeto; cout << "a = " << objeto.LeerA() << ", b = " << objeto.LeerB() << endl; system("pause"); return 0; } La salida es ésta: Constructor de A Constructor de B a = 10, b = 20 Presione una tecla para continuar . . . Destructor de B Destructor de A Se ve que primero se llama al destructor de la clase derivada B, y después al de la clase base A. MTP II C++ Herencia Superposición de Funciones: En una clase derivada se puede definir una función que ya existía en la clase base, esto se conoce como superposición de una función. La definición de la función en la clase derivada oculta la definición previa en la clase base. En caso necesario, es posible acceder a la función oculta de la clase base mediante su nombre completo: <objeto>.<clase_base>::<método>; class ClaseA { public: ClaseA() : datoA(10) {} int LeerA() const {return datoA;} void Mostrar() {cout << "a = " << datoA << endl;} (1) protected: int datoA; }; class ClaseB : public ClaseA { public: ClaseB() : datoB(20) {} int LeerB() const {return datoB;} void Mostrar() {cout << "a = " << datoA << ", b = " << datoB << endl;} (2) protected: int datoB; MTP II C++ }; Herencia int main() { ClaseB objeto; objeto.Mostrar(); objeto.ClaseA::Mostrar(); system("pause"); } return 0; La salida de este programa es: a = 10, b = 20 a = 10 Presione cualquier tecla para continuar . . . La definición de la función "Mostrar" en la ClaseB (1) oculta la definición previa de la función en la ClaseA (2). MTP II C++ Superposición y Sobrecarga Herencia Cuando se superpone una función, se ocultan todas las funciones con el mismo nombre en la clase base. Supongamos que hemos sobrecargado la función de la clase base que después volveremos a definir en la clase derivada. class ClaseA { public: void Incrementar() {cout << "Suma 1" << endl;} void Incrementar(int n) {cout << "Suma " << n << endl;} }; class ClaseB : public ClaseA { public: void Incrementar() {cout << "Suma 2" << endl;} }; MTP II C++ int main() { ClaseB objeto; } Herencia objeto.Incrementar(); // objeto.Incrementar(10); objeto.ClaseA::Incrementar(); objeto.ClaseA::Incrementar(10); system("pause"); return 0; La salida sería: Suma 2 Suma 1 Suma 10 Presione cualquier tecla para continuar . . . Ahora bien, no es posible acceder a ninguna de las funciones superpuestas de la clase base, aunque tengan distintos valores de retorno o distinto número o tipo de parámetros. Todas las funciones "incrementar" de la clase base han quedado ocultas, y sólo son accesibles mediante el nombre completo. MTP II C++ Herencia Polimorfismo: C++ nos permite acceder a objetos de una clase derivada usando un puntero a la clase base. En eso consiste el polimorfismo. Por supuesto, sólo podremos acceder a datos y funciones que existan en la clase base, los datos y funciones propias de los objetos de clases derivadas serán innacesibles. class Persona { public: Persona(char *n) { strcpy(nombre, n); } char *VerNombre(char *n) { strcpy(n, nombre); return n;} protected: char nombre[30]; }; class Empleado : public Persona { public: Empleado(char *n) : Persona(n) {} char *VerNombre(char *n) { strcpy(n, "Emp: "); strcat(n, nombre); return n;} }; class Estudiante : public Persona { public: Estudiante(char *n) : Persona(n) {} char *VerNombre(char *n) { strcpy(n, "Est: "); strcat(n, nombre); return n;} }; MTP II C++ Herencia int main() { char n[40]; Persona *Pepito = new Estudiante("Jose"); Persona *Carlos = new Empleado("Carlos"); } cout << Carlos->VerNombre(n) << endl; cout << Pepito->VerNombre(n) << endl; delete Pepito; delete Carlos; system("pause"); return 0; La salida es como ésta: Carlos Jose Presione cualquier tecla para continuar . . . Vemos que se ejecuta la versión de la función "VerNombre" que hemos definido para la clase base MTP II C++ Funciones Virtuales: Herencia El ejemplo anterior demuestra algunas de las posibilidades del polimorfismo, pero tal vez sería mucho más interesante que cuando se invoque a una función que se superpone en la clase derivada, se llame a ésta última función, la de la clase derivada. Esto se consigue mediante el uso de funciones virtuales. Cuando en una clase declaramos una función como virtual, y la superponemos en alguna clase derivada, al invocarla usando un puntero de la clase base, se ejecutará la versión de la clase derivada. virtual <tipo> <nombre_función>(<lista_parámetros>) [{}]; Modifiquemos en el ejemplo anterior la declaración de la clase base "Persona". class Persona { public: Persona(char *n) { strcpy(nombre, n); } virtual char *VerNombre(char *n) { strcpy(n, nombre); return n;} protected: char nombre[30]; }; MTP II C++ Herencia La salida es como ésta: Emp: Carlos Est: Jose Presione cualquier tecla para continuar . . . Ahora, al llamar a "Pepito->VerNombre(n)" se invoca a la función "VerNombre" de la clase "Estudiante", y al llamar a "Carlos->VerNombre(n)" se invoca a la función de la clase "Empleado". Una vez que una función es declarada como virtual, lo seguirá siendo en las clases derivadas, es decir, la propiedad virtual se hereda. Si la función virtual no se define exactamente con el mismo tipo de valor de retorno y el mismo número y tipo de parámetros que en la clase base, no se considerará como la misma función, sino como una función superpuesta. MTP II C++