Clase Polimorfismo
-
Upload
raul-leonardo-huaman-p -
Category
Documents
-
view
235 -
download
0
description
Transcript of Clase Polimorfismo
Programación Orientada a Objetos
(Clase 21.05.2015)
Prof. J.Fiestas
Programación Orientada a Objetos
3 principales elementos de la Programación Orientada a Objetos:
-‐ Abstracción de Objetos -‐ Herencia (y composición) -‐ Polimorfismo (funciones virtuales)
Programación Orientada a Objetos
ARBOL
plantar () cosechar()
CLASE
INTERFASE
arbol lucuma; lucuma.plantar(); lucuma.cosechar();
(nombre del) OBJETO: lucuma
UML: unified modeling language
ABSTRACCIÓN DE OBJETOS:
Programación Orientada a Objetos
HERENCIA Y COMPOSICION:
VEHICULO MOTORIZADO
costo_de_seguro() lista de repuestos()
VEHICULO Precio
lista de repuestos()
BICICLETA accesorios()
lista de repuestos()
AUTO costo_de_seguro() lista de repuestos()
MOTOCICLETA costo_de_seguro() lista de repuestos()
Programación Orientada a Objetos
POLIMORFISMO:
PINTURA
TECNICA
OLEO
TECNICA
ACUARELA
TECNICA
ACRILICO
TECNICA
Permite uXlizar disXntos métodos con el mismo nombre a traves de funciones virtuales
OLEO EN CANVAS
TECNICA
OLEO EN PAPEL
TECNICA
Programación Orientada a Objetos
//Instrument2.cpp // Inheritance & upcasXng #include <iostream> using namespace std; enum note { middleC, Csharp, Eflat }; // Etc. class Instrument { public: void play(note) const { cout << "Instrument::play" << endl; } }; // Wind objects are Instruments // because they have the same interface: class Wind : public Instrument { public: // Redefine interface funcXon: void play(note) const { cout << "Wind::play" << endl; } }; void tune(Instrument& i) { // ... i.play(middleC); }
int main() { Wind flute; tune(flute); // UpcasXng } ///:~
UpcasNng (conversión hacia arriba): tune() acepta un Instrument por referencia, y a todo lo derivado de Instrument. En el main(), el objeto Wind pasa a tune(), sin necesidad de un casXng
Polimorfismo:
Programación Orientada a Objetos
El problema: El output del programa anterior es Instrument::play, lo que no es lo ideal, ya que se sabe que Wind no es solo un Instrument sino uno de viento. Deberia imprimirse Wind::play
La solución en C++: C++ uXliza virtual para estos casos, declarando la función en la clase base. Solo la declaración necesita virtual (no la definición). Si una función es virtual en la clase base, es virtual en todas las clases derivadas Ahora la salida es Wind::play
//Instrument3.cpp // Late binding with the virtual keyword #include <iostream> using namespace std; enum note { middleC, Csharp, Cflat }; // Etc. class Instrument { public: virtual void play(note) const { cout << "Instrument::play" << endl; } }; // Wind objects are Instruments // because they have the same interface: class Wind : public Instrument { public: // Override interface funcXon: void play(note) const { cout << "Wind::play" << endl; } }; void tune(Instrument& i) { // ... i.play(middleC); }
int main() { Wind flute; tune(flute); // UpcasXng } ///:~
Polimorfismo:
Programación Orientada a Objetos
//Instrument4.cpp // Extensibility in OOP #include <iostream> using namespace std; enum note { middleC, Csharp, Cflat }; // Etc. class Instrument { public: virtual void play(note) const { cout << "Instrument::play" << endl; } virtual char* what() const { return "Instrument"; } // Assume this will modify the object: virtual void adjust(int) {} }; class Wind : public Instrument { public: void play(note) const { cout << "Wind::play" << endl; } char* what() const { return "Wind"; } void adjust(int) {} };
class Percussion : public Instrument { public: void play(note) const { cout << "Percussion::play" << endl; } char* what() const { return "Percussion"; } void adjust(int) {} }; class Stringed : public Instrument { public: void play(note) const { cout << "Stringed::play" << endl; } char* what() const { return "Stringed"; } void adjust(int) {} };
Polimorfismo:
Programación Orientada a Objetos
class Brass : public Wind { public: void play(note) const { cout << "Brass::play" << endl; } char* what() const { return "Brass"; } }; class Woodwind : public Wind { public: void play(note) const { cout << "Woodwind::play" << endl; } char* what() const { return "Woodwind"; } };
Aca se crea otro nivel de herencia, pero la clase virtual se manXene virtual en todas las clases derivadas Las clases Brass y Woodwind pueden llamar a la función adjust de la clase base
Polimorfismo:
Programación Orientada a Objetos
int main() { Wind flute; Percussion drum; Stringed violin; Brass flugelhorn; Woodwind recorder; tune(flute); tune(drum); tune(violin); tune(flugelhorn); tune(recorder); f(flugelhorn); } ///:~
tune() realiza upcasXng en cada Xpo diferente de objeto, obteniendo siempre el resultado correcto.
// IdenXcal funcXon from before: void tune(Instrument& i) { // ... i.play(middleC); } // New funcXon: void f(Instrument& i) { i.adjust(1); } // UpcasXng during array iniXalizaXon: Instrument* A[] = { new Wind, new Percussion, new Stringed, new Brass, };
Polimorfismo:
Programación Orientada a Objetos
Enlace dinámico:
Cuando se enlaza el llamado de una función en el main() a su declaración, se hace enlace por el compilador (enlace temprano). Por ello, en el ejemplo anterior, se llaman originalmente a las funciones de la clase base. La solución la dan las funciones virtuales a través del enlace dinámico, o tardío. Virtual, le dice al compilador que no debe realizar enlace temprano. Es decir, si se llama play() para un objeto de Xpo Brass, a través de una direccion a la clase base Instrument, se hara el enlace correcto.
Con ese objeXvo, el compilador crea una tabla (VTABLE) para cada clase que contenga funciones virtuales. En esa tabla se listan las direcciones de las funciones virtuales, a las cuales se les asigna un puntero (vpointer, VPTR), que apunta a la VTABLE de ese objeto (al hacer un llamado polimórfico), llamando asi a la función correcta (enlace dinámico)
Programación Orientada a Objetos
Enlace dinámico: Vector A de punteros a Instrument, incluye todas sus clases derivadas
Cada vez que se crea una clase con funciones virtuales se crea una VTABLE para esa clase, con las direcciones de las funciones declaradas virtuales (de no ser virtuales se usara la funcion de la clase base: adjust() in Brass)
Entonces uXliza el puntero virtual (VPTR) en la clase, que apunta a la direccion inicial de la tabla. Una vez inicializado en VTABLE, se define el Xpo de objeto que este es.
Programación Orientada a Objetos
Por ejemplo, el compilador inicia con el puntero de Xpo Instrumento, que apunta a la direccion inicial del objeto de Xpo Brass. El VPTR apunta a la direccion inicial de VTABLE, con direccion de funciones en un orden específico (el mismo orden para todas las VTABLE) La función adjust esta ubicada en VPTR+2
Programación Orientada a Objetos
Importante: UpcasXng funciona solo con direcciones, ya que si el compilador Xene un objeto, el Xpo ya esta definido y no se usará enlace dinámico En p1-‐>speak() y p2.speak() son usadas direcciones, o sea el Xpo no se conoce, y se hace uso de funciones virtuales. En p3.speak() no hay ambiguedad, y es una clase base, no derivada, y se aplica enlace temprano.
//Early.cpp // Early binding & virtual funcXons #include <iostream> #include <string> using namespace std; class Pet { public: virtual string speak() const { return ""; } }; class Dog : public Pet { public: string speak() const { return "Bark!"; } }; int main() { Dog ralph; Pet* p1 = &ralph; Pet& p2 = ralph; Pet p3; // Late binding for both: cout << "p1-‐>speak() = " << p1-‐>speak() <<endl; cout << "p2.speak() = " << p2.speak() << endl; // Early binding (probably): cout << "p3.speak() = " << p3.speak() << endl; } ///:~
Programación Orientada a Objetos
Clases Abstractas:
La clase base abstracta representa solo una interfase comun a su clase derivada (no es posible crear un objeto de la clase base). En este caso es obligación definir las funciones en las clases derivadas (en caso contrario seran tambien abstractas).
Programación Orientada a Objetos
Clases Abstractas: //Instrument5.cpp // Pure abstract base classes #include <iostream> using namespace std; enum note { middleC, Csharp, Cflat }; // Etc. class Instrument { public: // Pure virtual funcXons: virtual void play(note) const = 0; virtual char* what() const = 0; // Assume this will modify the object: virtual void adjust(int) = 0; }; // Rest of the file is the same ...
virtual void f() = 0;
Objetos de clase Instrument, no Xenen un significado real. Una declaración abstracta es como
Programación Orientada a Objetos
//AddingVirtuals.cpp // Adding virtuals in derivaXon #include <iostream> #include <string> using namespace std; class Pet { string pname; public: Pet(const string& petName) : pname(petName) {} virtual string name() const { return pname; } virtual string speak() const { return ""; } }; class Dog : public Pet { string name; public: Dog(const string& petName) : Pet(petName) {} // New virtual funcXon in the Dog class: virtual string sit() const { return Pet::name() + " sits"; } string speak() const { // Override return Pet::name() + " says 'Bark!'"; } };
Herencia y funciones virtuales en clases derivadas: Pet conXene dos funciones virtuales: speak() y name() Dog agrega una tercera función virtual sit() y sobrecarga la definición de speak()
Programación Orientada a Objetos
int main() { Pet* p[] = {new Pet("generic"),new Dog("bob")}; cout << "p[0]-‐>speak() = " << p[0]-‐>speak() << endl; cout << "p[1]-‐>speak() = " << p[1]-‐>speak() << endl; //! cout << "p[1]-‐>sit() = " //! << p[1]-‐>sit() << endl; // Illegal } ///:~
El compilador localiza speak() en el mismo lugar del VTABLE para Dog y Pet. Pero como p[] es un puntero a la clase objeto Pet, la que Xene solo las funciones speak() y name(), solo permiXra llamar a esas funciones. Una solución es hacer casXng al puntero: Pero hay que saber el Xpo del puntero para ello
((Dog*)p[1])-‐>sit()
Programación Orientada a Objetos
Object slicing:
Que pasa si pasamos objetos por valor a las funciones virtuales? Normalmente se usan punteros, y pasan direcciones que Xenen el mismo tamaño, independientemente a si son clases bases o derivadas (que son normalmente objetos mas grandes) Si se hace upcasXng a un objeto (por valor) y no a un puntero o referencia. El objeto es cortado (sliced)
//ObjectSlicing.cpp #include <iostream> #include <string> using namespace std; class Pet { string pname; public: Pet(const string& name) : pname(name) {} virtual string name() const { return pname; } virtual string descripXon() const { return "This is " + pname; } }; class Dog : public Pet { string favoriteAcXvity; public: Dog(const string& name, const string& acXvity) : Pet(name), favoriteAcXvity(acXvity) {} string descripXon() const { return Pet::name() + " likes to " + favoriteAcXvity; } };
Programación Orientada a Objetos
void describe(Pet p) { // Slices the object cout << p.descripXon() << endl; } int main() { Pet p("Alfred"); Dog d("Fluffy", "sleep"); describe(p); describe(d); } ///:~
La función describe() pasa un objeto de Xpo Pet por valor. Este llama la función virtual descripXon() para el objeto Pet. En main(), ambos llamados usaran la version de descripXon() de la clase base. Ya que describe() acepta un objeto Pet, esta va a transformar al objeto derivado de Pet en un objeto Pet, pero solo copiando la parte Pet del objeto
Programación Orientada a Objetos
Ejercicio: Escribir un programa simple en C++ uXlizando polimorfismo, que describa el diagrama de herencia de la pagina 4 de la clase de hoy, y que ayude al usuario a decidir que Xpo de vehiculo comprar de acuerdo a la canXdad de dinero disponible durante el primer año. Para ello: -‐ Declarar la funcion lista_de_respuestos() como virtual, que retorne una lista de 3 repuestos con sus respecXvos costos, dependiendo del vehiculo que se trate. -‐ Definir una funcion virtual costo_de_seguro(), en el caso de vehiculos motorizados (clase derivada) que retorne el costo de seguro mensual -‐ Definir ademas una funcion virtual que calcule el costo de repuestos por año, asumiendo el consumo de un Xpo de repuesto por mes. -‐ Una funcion externa ayudara a decidir que Xpo de vehiculo puedo comprar de acuerdo a una canXdad de dinero ingresada por el usuario, y que retorne: el precio del vehiculo elejido, los costos de repuestos por año, y los costos de seguro anual en caso sea un vehiculo motorizado. Definir los objetos en el main() como punteros de clase Vehiculo, por ejemplo: Vehiculo *p = new Bicicleta(precio,capacidad);