Programación I Unidad 6 Polimorfismo Polimorfismo •Permite diseñar e implementar sistemas que sean extensibles con gran facilidad. •Los programas se escriben para que procesen objetos de forma genérica: objetos de todas las clases existentes en una jerarquía. A B C D Polimorfismo • Un puntero de tipo A puede apuntar a cualquier objeto de sus subclases (no al contrario). A=animales; B=perros; C=gatos; D=leones; •Tiene sentido: ¿Un perro es un animal? SI A* a = new B(); // CORRECTO ¿Todos los animales son perros? NO B* b = new A(); // INCORRECTO Polimorfismo y métodos virtuales Supongamos que la clase A contiene un método estático p, y que las subclases de A redefinen dicho método: C* objC = new C(); B* objB = new B(); A* objA = objB; objA->p(); // (1) objA = objC; objA->p(); // (2) Tanto en (1) como en (2) se ejecuta el método p de la clase A. La razón es que objA es de tipo A, y como el método es de enlace estático, siempre se ejecuta el método de la clase a la que pertenece la variable y no el de la clase del objeto contenido en la variable (enlace dinámico-tiempo de ejecución). Polimorfismo y métodos virtuales ¿qué podríamos hacer para que se ejecute el método del tipo del objeto contenido en la variable? métodos virtuales “Se determina el método a ejecutar en tiempo de ejecución” ENLACE DINÁMICO (SÓLO POSIBLE CON PUNTEROS Y REFERENCIAS) Sintaxis: virtual tipo nombreMetodo(parámetros); Convenio: Poner virtual, además de en la clase base, en todas las subclases directas o indirectas que redefinan el método. Polimorfismo y métodos virtuales: ejemplo class Base { public: virtual void p(){ShowMessage(“Clase base");} }; class Hija:public Base { public: virtual void p(){ShowMessage(“Clase hija");} }; class Nieta:public Hija { public: virtual void p(){ShowMessage(“Clase nieta");} }; Base* b=new Hija(); b->p(); // p de la clase Hija Hija* h = new Nieta(); h->p(); // p de la clase Nieta Aunque en las subclases el método p no apareciera como virtual lo sería igualmente. Polimorfismo y métodos nuevos Cuando una subclase añade nuevos métodos, si hacemos uso del polimorfismo, no podemos invocarlos directamente: hay que hacer “casting”. class Base { public: virtual void p() {ShowMessage(“Clase base");} }; class Hija:public Base { public: virtual void p(){ShowMessage(“Clase hija");} void p1(){ShowMessage(“Soy nuevo”);} }; Base * b = new Hija(); b->p1(); // error de compilación (Hija* b)->p1(); //se ejecuta el método p1 de la clase Hija Polimorfismo: Destructores class Base{ private: T* t; // objeto dinámico de la clase T public: Base(){t=new T(...);....} ..... ~Base(){delete t; t=NULL;}}; class Hija:public Base { private: M* m; // objeto dinámico de la clase M public: Hija():Base(){m=new M(...);...} ..... ~Hija(){delete m; m=NULL;}}; Base* b = new Hija(); delete b; Se invoca al destructor de la clase Base !!! Dejamos sin borrar m !!! SOLUCIÓN: EL destructor de la clase Base debe ser virtual. En tal caso, primero se ejecutará la destructora de la clase Hija, y después la de la clase Base. Polimorfismo: clases abstractas Una clase abstracta es una clase de la que no podemos tener instancias (i.e. objetos). Se usan como clases “tipo” de las cuales heredan otras clases. Para indicar que una clase es abstracta basta introducir un método virtual puro (o abstracto). virtual tipo nombreMetodo(argumentos)=0; Por ejemplo: class Base { public: virtual void show()=0; }; Una clase que herede de la clase Base debe implementar el método show. En otro caso la subclase también se considerará abstracta. Polimorfismo: Información de tipo en ejecución La función typeid devuelve una referencia a un objeto de la clase type_info. Los objetos de esta clase representan un tipo. Sintaxis: typeid(expresión) == typeid(tipo) Ejemplos: Grafico* graf = new Rectangulo(......); if typeid(*graf)==typeid(Rectangulo) ShowMessage(“cierto”); else ShowMessage(“falso”); Si la clase Grafico contiene al menos un método virtual (clase polimórfica), se mostrará “cierto”, en otro caso se mostrará “falso”. Polimorfismo: Información de tipo en ejecución Con clases polimórficas, typeid(*graf) devuelve el tipo del objeto apuntado por graf en tiempo de ejecución. En otro caso devuelve el tipo en tiempo de compilación. La clase type_info tiene definidos los operadores “==“ y “!=“ Para utilizar typeid es necesario incluir el módulo typeinfo: #include <typeinfo> typeid(*a) con a == NULL genera una excepción.