Professional Documents
Culture Documents
Curso 2008-2009
Herencia y Polimorfismo
Herencia Concepto y justificacin de la herencia. Jerarquas de herencia. mbitos y acceso con herencia. Criterios de usos de la herencia. Polimorfismo Definicin y uso del polimorfismo. Ligadura dinmica
Clases y Objetos
+
Herencia Polimorfismo
TEMA 4. HERENCIA Y POLIMORFISMO 3
Clase Base
Clase Derivada
Las subclases pueden redefinir los mtodos y atributos de la clase padre y aadir otros nuevos.
es un
Polgono
vrtice[] rea() permetro()
es un
Crculo
radio rea() permetro()
Los objetos instancias de las subclases contienen todos los datos y operaciones de sus superclases y por tanto pueden realizar todas las operaciones que stas realizan. Al revs no es cierto: Un objeto de una superclase puede no tener todas las operaciones de sus clases derivadas.
Jerarquas de herencia
Las subclases de una clase pueden ser a su vez superclases de otras clases. Esto da lugar a jerarquas de clases.
Jerarquas de Generalizacin/Especializacin
Figura
Especializacin es-un
Punto Polgono Recta
Generalizacin
es-un
Tringulo
Cuadrado
Pentgono
En C++ una clase puede derivar de una nica clase o de varias. Herencia Simple Herencia Mltiple
TEMA 4. HERENCIA Y POLIMORFISMO
class Poligono: public Figura{//clase derivada public: double area(){ ... } double perimetro(){ ... } protected: Punto vertice[]; };
Miembros protected
El acceso protected ofrece un nivel intermedio de proteccin entre el acceso public y el private. Los miembros y friend de la clase base y los miembros y friend de la clases derivadas de la base son los nicos que pueden acceder a los miembros protected de esa clase base. Los miembros de las clases derivadas pueden hacer referencia a los miembros public y protected de su clase base mediante los nombres de los miembros. Si un miembro de la clase base se ha redefinido en la clase derivada y se quiere acceder al de la clase base hay que anteponer el nombre de la clase base al miembro y el operador ::
Pero, problema:
Los miembros no privados (private) que tampoco son pblicos (public) slo private public son accesibles dentro de su propio paquete. Y si la subclase se define en otro paquete?
Con objeto de hacer visibles a las subclases variables y mtodos de la superclase se utiliza el especificador de acceso: protected
Clase Punto2, utilizada como clase base de Circulo3 (se utilizan datos protected)
// La definicin de la CLASE PUNTO2 representa un par de coordenadas x-y. #include <iostream.h> class Punto2 { public: Punto2( int = 0, int = 0 ); //constructor predeterminado void estableceX( int ); int obtieneX() const; void estableceY( int ); int obtieneY() const; void imprime() const; // despliega el objeto Punto2 protected: int x; // parte x del par de coordenadas int y; // parte y del par de coordenadas }; // fin de la clase Punto2 void Punto2::estableceX( int valorX ) { x = valorX; } // fin de la funcin estableceX int Punto2::obtieneX() const { return x; } // fin de la funcin obtieneX void Punto2::estableceY( int valorY ) { y = valorY; } // fin de la funcin estableceY int Punto2::obtieneY() const { return y; } // fin de la funcin obtieneY // muestra el objeto Punto2 void Punto2::imprime() const { cout << '[' << x << ", " << y << ']'; } // constructor predeterminado Punto2::Punto2( int valorX, int valorY ) { x = valorX; y = valorY; } // fin del constructor Punto2
Clase Circulo 3 que hereda la clase Punto2 (se utilizan datos protected)
class Circulo3 : public Punto2 { public: // constructor predeterminado Circulo3( int = 0, int = 0, double = 0.0 ); void estableceRadio( double ); // establece el radio double obtieneRadio() const; // devuelve el radio double obtieneDiametro() const; double obtieneCircunferencia() const; double obtieneArea() const; // devuelve el rea void imprime() const; private: double radio; }; // fin de la clase Circulo3 // despliega el objeto Circulo3 Circulo3::Circulo3( int valorX, int valorY, double valorRadio) { x = valorX; // si no fueran los datos protected no se y = valorY; // podria hacer, al igual que en imprime estableceRadio( valorRadio ); } void Circulo3::estableceRadio( double valorRadio ) { radio = ( valorRadio < 0.0 ? 0.0 : valorRadio ); } double Circulo3::obtieneRadio() const { return radio; } double Circulo3::obtieneDiametro() const { return 2 * radio; } double Circulo3::obtieneCircunferencia() const { return 3.14159 * obtieneDiametro(); } double Circulo3::obtieneArea() const { return 3.14159 * radio * radio; } void Circulo3::imprime() const { cout << "Centro = [" << x << ", " << y << "]" << "; Radio = " << radio; }
// radio de Circulo3
Clase Punto3, utilizada como clase base de Circulo4 (se utilizan datos private)
// La definicin de la CLASE PUNTO3 representa un par de coordenadas x-y. #include <iostream.h> class Punto3 { public: Punto3( int = 0, int = 0 ); //constructor predeterminado void estableceX( int ); int obtieneX() const; void estableceY( int ); int obtieneY() const; void imprime() const; // despliega el objeto Punto3 private: int x; // parte x del par de coordenadas int y; // parte y del par de coordenadas }; // fin de la clase Punto3 void Punto3::estableceX( int valorX ) { x = valorX; } // fin de la funcin estableceX int Punto3::obtieneX() const { return x; } // fin de la funcin obtieneX void Punto3::estableceY( int valorY ) { y = valorY; } // fin de la funcin estableceY int Punto3::obtieneY() const { return y; } // fin de la funcin obtieneY // muestra el objeto Punto3 void Punto3::imprime() const { cout << '[' << x << ", " << y << ']'; } // constructor predeterminado Punto3::Punto3( int valorX, int valorY ) : x( valorX ), y( valorY ) { /* cuerpo vaco*/ } // fin del constructor Punto3
Clase Circulo 4 que hereda la clase Punto3 (se utilizan datos private)
class Circulo4 : public Punto3{ public: // constructor predeterminado Circulo4( int = 0, int = 0, double = 0.0 ); void estableceRadio( double ); // establece el radio double obtieneRadio() const; // devuelve el radio double obtieneDiametro() const; double obtieneCircunferencia() const; double obtieneArea() const; // devuelve el rea void imprime() const; private: double radio; }; // fin de la clase Circulo4 // despliega el objeto Circulo4 Circulo4::Circulo4( int valorX, int valorY, double valorRadio ) : Punto3( valorX, valorY ) // constructor de la clase base { estableceRadio( valorRadio ); } void Circulo4::estableceRadio( double valorRadio ) { radio = ( valorRadio < 0.0 ? 0.0 : valorRadio ); } double Circulo4::obtieneRadio() const { return radio; } double Circulo4::obtieneDiametro() const { return 2 * radio; } double Circulo4::obtieneCircunferencia() const { return 3.14159 * obtieneDiametro(); } double Circulo4::obtieneArea() const { return 3.14159 * radio * radio; } void Circulo4::imprime() const { cout << "Centro = "; Punto3::imprime(); // funcin imprime de Punto3 cout << "; Radio = " << obtieneRadio();}
// radio de Circulo4
Polimorfismo
Polimorfismo
El trmino polimorfismo se refiere a que una caracterstica de una clase puede tomar varias formas, en P.O.O. representa:
Ejemplos con punteros del tipo de la clase base y de la clase derivada siendo declarada virtual la funcin imprime()
Si modificamos el cdigo de la clase base Punto, que est en la diapositiva n 14, colocando la palabra virtual delante de la funcin miembro imprime() virtual void imprime() const; // despliega el objeto Punto3 Y ejecutamos de nuevo el cdigo main de la diapositiva n 18, comprobaremos que el ltimo imprime, no va a imprimir el Punto, sino que imprime el Circulo, y nos encontramos con dos sentencias iguales , la del primer imprime(): puntoPtr->imprime(); // invoca a la funcin de Punto y la del ltimo puntoPtr->imprime(); // ahora invoca a la funcin de Circulo Dos sentencias iguales: Producen distintos resultados: Dependiendo de que: puntoPtr->imprime(); Se invoca a una funcin miembro de un objeto Punto o de otro Circulo puntoPtr apunte a uno u otro objeto.
Haciendo la funcin virtual, la funcionalidad invocada depende del tipo del objeto no del tipo del manejador.
Salidas por pantalla de la diapositiva n 18, utilizando la herencia formada por las clases Punto y Circulo da la diapositivas n 14 y 15 respectivamente. La primera sin funcin virtual, y la segunda con funcin virtual
Cuando se invoca a una funcin virtual por medio del nombre de un objeto y utilizando el operador punto, la funcin que se invoca se resuelve en tiempo de compilacin y se llama ligadura esttica, la funcin que se invoca es la que simplemente se ha heredado. La ligadura dinmica slo se da utilizando apuntadores o referencias.
Polimorfismo
Si una clase rectngulo se deriva de una clase cuadriltero, una operacin como el clculo del permetro o rea puede realizarse por objetos de ambas clases. El polimorfismo en C++ se implementa por medio de funciones virtual. Para llamar a la funcin permetro de la clase base cuadriltero para un objeto de la clase derivada, la funcin se debe llamar explcitamente. Ptr -> cuadrilatero::perimetro();
Notas:
El uso de funciones virtuales y el polimorfismo permite que el programador maneje generalidades y dejar que el entorno de programacin en tiempo de ejecucin se encargue de asuntos especficos. El polimorfismo promueve la extensibilidad del software
Funciones Virtual
Supongamos un conjunto de clases derivadas de la clase formas: cuadrado, circulo, etc. C++ permite dar a cada clase la posibilidad de dibujarse a s misma (funcin dib), pero esta funcin es diferente en cada clase. Una funcin virtual permite llamar a la funcin dib de la clase formas y que dinmicamente(en tiempo de ejecucin) el programa determine que funcin dib debe utilizar. Una funcin virtual se define:
Se declara en la clase base y se superpone en las clase derivadas Si utilizamos un puntero para realizar la Formasptr -> dib()
llamada a la funcin dib, el programa seleccionar de manera dinmica la funcin dib de la clase derivada correcta.
TEMA 4. HERENCIA Y POLIMORFISMO
23
Ejemplo de Polimorfismo
Polimorfismo
class cuadrilatero {
public:
//funciones virtuales
virtual double area(){return 0.0} virtual double perimetro(){return 0.0} virtual void printName() const = 0;
};
class cuadrado: public cuadrilatero { public: Cuadrado (double=0.0); virtual double area(){ return l * l; } virtual double perimetro(){ return l * 4;} virtual void printName() const {cout << Cuadrado: ;} private: double l;
class rectangulo: public cuadrilatero { public: Rectangulo (double=0.0, double=0.0,); virtual double area(){ return la * lb; } virtual double perimetro(){ return 2*la+2lb;} virtual void printName() const {cout << Rectangulo: ;} private: double la, lb;
Ejemplo de Polimorfismo
Polimorfismo
void virtualPtr (const Cuadrilatero *); void virtualRef (const Cuadrilatero &);
}
TEMA 4. HERENCIA Y POLIMORFISMO 25
Ejemplo de Polimorfismo
Polimorfismo
// Llamadas de funcin virtual a partir del ptr de clase base utilizando enlace dinmico void virtualPtr (const Cuadrilatero *PtrClaseBase) { PtrClaseBase -> printName(); cout << \n rea = << PtrClaseBase->area() << \n permetro = << PtrClaseBase-> perimetro()<<\n\n;
}
// Llamadas de funcin virtual a partir de la referencia de clase base utilizando enlace dinmico void virtualRef (const Cuadrilatero &RefClaseBase){ RefClaseBase.printName(); cout << \n rea = << RefClaseBase.area() << \n permetro = << RefClaseBase.perimetro()<<\n\n;
Polimorfismo
PtrClaseBase -> printName() PtrClaseBase -> area() PtrClaseBase -> perimetro()
Ejemplo de Polimorfismo
Cada una invoca a la funcin virtual sobre el objeto hacia el cual apunta PtrClaseBase. El cuadro siguiente muestra la salida producida al utilizar punteros de clase base
Llamada a funciones virtuales mediante punteros a la clase base Cuadrado: rea = 4.0 permetro = 8.0 Rectngulo: rea = 6.0 permetro = 10.0
Polimorfismo
RefClaseBase .printName() RefClaseBase .area() RefClaseBase .perimetro()
Ejemplo de Polimorfismo
Cada llamada de estas funciones llama a estas funciones sobre el objeto al cual hace referencia RefClaseBase. La salida producida al utilizar referencias de clase base es la misma que al utilizar punteros de clase base
Llamada a funciones virtuales mediante referencias a la clase base Cuadrado: rea = 4.0 permetro = 8.0 Rectngulo: rea = 6.0 permetro = 10.0
Ligadura dinmica
Un mensaje con una determinada signatura puede enviarse a cualquier objeto que lo incluya en su interfaz. Pero como diferentes objetos pueden implementar un mismo mtodo de diferente manera, el resultado del mensaje depende no slo del mensaje, sino de su receptor. Un mtodo puede tener tantas formas como objetos de clases diferentes lo incluyan en su interfaz.
POLIMORFISMO
El enlace dinmico significa que el receptor del mensaje se determina durante la ejecucin del programa.
Por tanto: Es posible seleccionar el receptor durante la ejecucin del programa. Polimorfismo y enlace dinmico no son exactamente lo mismo. El enlace dinmico es el mecanismo que hace posible implementar el polimorfismo.
TEMA 4. HERENCIA Y POLIMORFISMO 29
Clase Abstracta
Una funcin virtual pura es aquella que no se implementa o est vaca, pero contiene un inicializador =0 en su declaracin: virtual printName() const = 0; // Identifica la funcin virtual pura Una clase que tiene funciones virtuales puras se denomina Clase Abstracta. Es una clase que no sirve para crear objetos (ya que las funciones virtuales puras no estn implementadas). Las Clases Abstractas se suele utilizar como clase base de una herencia, en este caso las funciones virtuales puras se tienen que sobrescribir en las clases derivadas para que esas clases derivadas sean concretas.