Tema3

453 views
335 views

Published on

0 Comments
0 Likes
Statistics
Notes
  • Be the first to comment

  • Be the first to like this

No Downloads
Views
Total views
453
On SlideShare
0
From Embeds
0
Number of Embeds
3
Actions
Shares
0
Downloads
1
Comments
0
Likes
0
Embeds 0
No embeds

No notes for slide

Tema3

  1. 1. Programación Avanzada - Tema 3: Programación orientada a objetos http://www3.uji.es/~llopis/II17 José Luis Llopis Borrás 15 de noviembre de 2004
  2. 2. Índice1. Introducción 4 1.1. La herencia en el mundo real . . . . . . . . . . . . . . . . . . . . . . . 5 1.2. La herencia y la teoría de conjuntos . . . . . . . . . . . . . . . . . . . . 6 1.3. El principio de los subtipos . . . . . . . . . . . . . . . . . . . . . . . . . 72. La herencia en la POO 8 2.1. Jerarquía de clases . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103. Herencia pública en C++ 12 3.1. Clases derivadas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14 3.2. Utilización de clases base y derivadas . . . . . . . . . . . . . . . . . . . 18 3.3. El principio de los subtipos en C++ . . . . . . . . . . . . . . . . . . . . 20 3.4. Acceso a los miembros de la clase base . . . . . . . . . . . . . . . . . . 21 3.5. Miembros protegidos en la clase base . . . . . . . . . . . . . . . . . . . 22 3.6. Redefinición de miembros de la clase base . . . . . . . . . . . . . . . . 25 3.7. Herencia y constructores . . . . . . . . . . . . . . . . . . . . . . . . . . 31
  3. 3. 3.8. Ligadura estática vs. dinámica . . . . . . . . . . . . . . . . . . . . . . . 42 3.9. Funciones virtuales . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44 3.9.1. Invocación de funciones virtuales a través de un puntero . . . . . 45 3.9.2. Invocación de funciones virtuales a través de una referencia . . . 474. Caso práctico: aplicación gráfica 49 4.1. Identificación de objetos . . . . . . . . . . . . . . . . . . . . . . . . . . 50 4.2. Diseño de la jerarquía de clases . . . . . . . . . . . . . . . . . . . . . . 51 4.3. Diseño de la clase base . . . . . . . . . . . . . . . . . . . . . . . . . . 52 4.4. Diseño de las subclases . . . . . . . . . . . . . . . . . . . . . . . . . . 53 4.5. Estructura de datos heterogénea . . . . . . . . . . . . . . . . . . . . . 55 4.6. Operaciones globales . . . . . . . . . . . . . . . . . . . . . . . . . . . 56 4.7. Ligadura estática . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57 4.8. Funciones virtuales . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58 4.9. Ligadura dinámica . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59 4.10.Clases abstractas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62 4.11.Conclusiones del caso práctico . . . . . . . . . . . . . . . . . . . . . . 63
  4. 4. 1 Introducción ® Los seres humanos realizamos abstracciones de acuerdo con dos tipos de relaciones: parte-de y tipo-de. µ Decimos que un león tiene cuatro patas, cola larga, dientes y uñas fuertes, etc. µ Pero, seguramente, como mejor definimos un león es diciendo que es-un tipo de mamífero carnívoro, concretamente un gran felino. ® En el ámbito de los lenguajes de programación hasta ahora sólo disponíamos de una herramienta para expresar el primer tipo de relaciones: µ La composición: permite expresar relaciones parte-de (o tiene-un). Los registros de C no son más que eso, una herramienta de composición. ® La programación orientada a objetos introduce una nueva herramienta para expresar el segundo tipo de relaciones: µ La herencia: permite expresar las relaciones tipo-de (o es-un).Programación Avanzada - Tema 3: Programación orientada a objetos – 4
  5. 5. 1.1 La herencia en el mundo real ® En el ejemplo siguiente, establecemos una organización jerárquica de distintos tipos de animales: µ Un león es un mamífero. µ Un león es un subtipo del tipo mamífero. µ El tipo mamífero es un supertipo de los tipos Gato y León. Animal Tipo Categoría Mamífero Ave Subtipo Subcategoría Gato León Águila PalomaProgramación Avanzada - Tema 3: Programación orientada a objetos – 5
  6. 6. 1.2 La herencia y la teoría de conjuntos ® El conjunto de elementos que pertenecen a un tipo incluye a los elementos que pertenezcan a sus subtipos. Animal Mamífero Ave Conjuntos anidados Gato León Águila Paloma Relación entre tipos y subtipos Edificio Motocicleta Conjuntos disjuntos No hay relación de subtipado entre estos tiposProgramación Avanzada - Tema 3: Programación orientada a objetos – 6
  7. 7. 1.3 El principio de los subtipos ® Principio de subtipos: “Un objeto de un subtipo puede aparecer en cualquier lugar donde se espera que aparezca un objeto del supertipo”. µ Los animales son capaces de moverse por sí mismos. µ Los mamíferos son capaces de moverse por sí mismos. µ Las aves son capaces de moverse por sí mismas. µ Los gatos son capaces de moverse por sí mismos. ® A la inversa no es cierto. µ Los gatos maullan. µ ¿Los mamíferos maullan? µ ¿Los animales maullan?Programación Avanzada - Tema 3: Programación orientada a objetos – 7
  8. 8. 2 La herencia en la POO ® La herencia es un recurso fundamental en los lenguajes orientados a objetos. ® Esencialmente consiste en definir una nueva clase: µ como extensión de otra previamente definida, y µ sin modificar la ya existente. ® La nueva clase hereda de la clase base: µ los atributos (variables miembro de la clase), y µ los métodos (funciones miembro de la clase). ® Principal beneficio: la reutilización del código. µ Ahorro de esfuerzo. µ Mayor confianza en el código.Programación Avanzada - Tema 3: Programación orientada a objetos – 8
  9. 9. 2 La herencia en la POO (II) ® La clase derivada es-un tipo especial de clase base. µ Un Mecánico es-un (tipo especial de) Empleado. µ Un Comercial es-un (tipo especial de) Empleado. Clase base Empleado superclase Empleado Mecánico Comercial Clase derivada Mecánico Comercial subclaseProgramación Avanzada - Tema 3: Programación orientada a objetos – 9
  10. 10. 2.1 Jerarquía de clases ® La herencia permite establecer jerarquías de clases relacionadas entre sí. Empleado sueldoFijo ® En el ejemplo, las subclases asignarSueldo(s) heredan los datos y las sueldoBase() operaciones de la clase base. Mecanico Comercial numPiezas ventas ® Los objetos de una clase precioPorPieza derivada pueden acceder a asignarNumPrecio(n, p) asignarVentas(v) sueldo() sueldo() los miembros públicos heredados de la clase base (Versión 1) de igual forma que acceden a los propios.Programación Avanzada - Tema 3: Programación orientada a objetos – 10
  11. 11. 2.1 Jerarquía de clases (II) ® Un objeto m de la clase Mecanico es además un Empleado, por tanto contiene todos los miembros de la clase base (ya sean datos o funciones) y además los propios miembros definidos en la clase derivada. Objeto m propia heredada Interfaz Interfaz asignarSueldo(s) sueldoFijo Dato sueldoBase() heredado numPiezas asignarNumPrecio(n, p) Datos precioPorPieza propios sueldo()Programación Avanzada - Tema 3: Programación orientada a objetos – 11
  12. 12. 3 Herencia pública en C++ ® Vamos a realizar la implementación completa de la jerarquía formada por las clases Empleado, Mecanico y Comercial. Comenzamos con la clase base. empleado.h (versión 1) # i f n d e f EMPLEADO_H # define EMPLEADO_H class Empleado { private : float sueldoFijo ; public : void a s i gn ar S ue ld o ( f l o a t ) ; float sueldoBase ( ) const ; }; #endifProgramación Avanzada - Tema 3: Programación orientada a objetos – 12
  13. 13. 3 Herencia pública en C++ (II) ® La implementación de la clase base no presenta ninguna particularidad: empleado.cpp (versión 1) # include "empleado.h" void Empleado : : as ig na r Su el d o ( f l o a t s ) { sueldoFijo = s ; } f l o a t Empleado : : sueldoBase ( ) const { return sue ldoFi jo ; }Programación Avanzada - Tema 3: Programación orientada a objetos – 13
  14. 14. 3.1 Clases derivadas ® Un mecánico es-un tipo especial de empleado, por lo tanto, definimos la mecanico.h (versión 1) clase Mecanico como una subclase # i f n d e f MECANICO_H # define MECANICO_H de Empleado. ® En C++ expresamos esta relación # include "empleado.h" utilizando herencia pública. También class Mecanico : public Empleado { es posible utilizar otros tipos de private : int numPiezas ; herencia, pero sólo la herencia f l o a t precioPorPieza ; pública expresa la relación es-un. public : void asignarNumPrecio ( i n t , f l o a t ) ; ® Con la herencia pública cada f l o a t sueldo ( ) const ; miembro de la clase base es }; heredado en la subclase #endif manteniendo sus privilegios de acceso.Programación Avanzada - Tema 3: Programación orientada a objetos – 14
  15. 15. 3.1 Clases derivadas (II) ® En la implementación de la subclase Mecanico podemos utilizar cualquier miembro público de la clase base (p.e. sueldoBase()). ® Cuando el compilador encuentra la llamada a la función sueldoBase() primero busca la función en la clase Mecanico y, como no existe, la busca en la clase base. mecanico.cpp (versión 1) # include "mecanico.h" void Mecanico : : asignarNumPrecio ( i n t n , f l o a t p ) { numPiezas = n ; precioPorPieza = p ; } f l o a t Mecanico : : sueldo ( ) const { f l o a t complementos = numPiezas ∗ p r e c i o P o r P i e z a ; r e t u r n sueldoBase ( ) + complementos ; }Programación Avanzada - Tema 3: Programación orientada a objetos – 15
  16. 16. 3.1 Clases derivadas (III) ® Un comercial es-un tipo especial de empleado, por lo tanto, definimos la clase Comercial como una subclase de Empleado. comercial.h (versión 1) # i f n d e f COMERCIAL_H # define COMERCIAL_H # include "empleado.h" class Comercial : public Empleado { private : f l o a t ventas ; public : void a si gn ar V en ta s ( f l o a t ) ; f l o a t sueldo ( ) const ; }; # e n d i f COMERCIALProgramación Avanzada - Tema 3: Programación orientada a objetos – 16
  17. 17. 3.1 Clases derivadas (IV) ® Por último, implementamos la clase Comercial. ® De nuevo, desde una función miembro de la clase Comercial, podemos llamar a una función de la clase base: un Comercial no deja de ser al mismo tiempo un Empleado. comercial.cpp (versión 1) # include "comercial.h" void Comercial : : as ig n ar Ve nt a s ( f l o a t v ) { ventas = v ; } f l o a t Comercial : : sueldo ( ) const { f l o a t complementos = ventas ∗ 0 . 1 0 ; r e t u r n sueldoBase ( ) + complementos ; }Programación Avanzada - Tema 3: Programación orientada a objetos – 17
  18. 18. 3.2 Utilización de clases base y derivadas test.cpp (versión 1) # include < iostream > # include "empleado.h" # include "mecanico.h" # include "comercial.h" using namespace s t d ; void f ( Empleado e ) { c o u t < < "Desde f() e.sueldoBase() = " < < e . sueldoBase ( ) < < e n d l ; } i n t main ( ) { Empleado e ; e . as ig na r Su el d o ( 1 0 0 0 ) ; c o u t < < "Sueldo del empleado: " < < e . sueldoBase ( ) < < e n d l ; Mecanico m; m. a s i gn a r S ue l do ( 2 0 0 0 ) ; m. asignarNumPrecio ( 1 0 0 , 3 ) ; c o u t < < "Sueldo del mecánico: " < < m. sueldo ( ) < < e n d l ;Programación Avanzada - Tema 3: Programación orientada a objetos – 18
  19. 19. 3.2 Utilización de clases base y derivadas Comercial c ; c . as ig na r Su el do ( 3 0 0 0 ) ; c . as ig na r Ve nt as ( 2 0 0 0 ) ; c o u t < < "Sueldo del comercial: " < < c . sueldo ( ) < < e n d l ; f (e) ; f (m) ; f (c) ; } salida (versión 1) Sueldo d e l empleado : 1000 Sueldo d e l mecánico : 2300 Sueldo d e l c o m e r c i a l : 3 2 0 0 Desde f ( ) e . sueldoBase ( ) = 1 0 0 0 Desde f ( ) e . sueldoBase ( ) = 2 0 0 0 Desde f ( ) e . sueldoBase ( ) = 3 0 0 0Programación Avanzada - Tema 3: Programación orientada a objetos – 19
  20. 20. 3.3 El principio de los subtipos en C++ ® El principio de los subtipos puede enunciarse de nuevo para C++ de la manera siguiente: Una clase derivada puede aparecer en cualquier lugar donde se espera una clase base pública. ® Efectivamente: la función f() de test.cpp está definida para recibir objetos de la clase Empleado. Sin embargo, también podemos llamarla con objetos de las clases Mecánico y Comercial. ® En todo caso, desde dentro de la función f() sólo podemos acceder a los miembros públicos de la clase base. La siguiente implementación de la función f() es incorrecta puesto que sueldo() no es una función de la clase Empleado: void f ( Empleado e ) { / / i n c o r r e c t o c o u t < < "Desde f() e.sueldo() = " < < e . sueldo ( ) < < e n d l ; }Programación Avanzada - Tema 3: Programación orientada a objetos – 20
  21. 21. 3.4 Acceso a los miembros de la clase base ® Hemos dicho que la clase derivada hereda todos los miembros de la clase base, pero ¿qué ocurre con los niveles de acceso? µ private Los miembros privados de la clase base no son accesibles en la clase derivada (ni en el exterior). µ public Los miembros públicos de la clase base son accesibles desde la clase derivada (y desde el exterior). ® Disponemos, además, de un nivel intermedio: µ protected Los miembros protegidos en la clase base: ª son accesibles en la clase derivada. ª no son accesibles desde el exterior. Recuerda que estamos hablando en el ámbito de la herencia pública.Programación Avanzada - Tema 3: Programación orientada a objetos – 21
  22. 22. 3.5 Miembros protegidos en la clase base ® La función sueldo() de las clases Mecanico y Comercial invoca a la función sueldoBase() de la clase Empleado para averiguar cuál es el sueldo fijo del empleado. Lo hacemos así porque no podemos acceder al dato privado sueldoFijo de la clase base. ® Pero, ¿y si sueldoFijo en lugar de privado fuera protegido? En ese caso: µ sería accesible desde la función sueldo() de las subclases, µ y, al mismo tiempo, se mantendría inaccesible desde el exterior.Programación Avanzada - Tema 3: Programación orientada a objetos – 22
  23. 23. 3.5 Miembros protegidos en la clase base (II) ® Declaramos el miembro sueldoFijo como protected: empleado.h (versión 1 bis) # i f n d e f EMPLEADO_H # define EMPLEADO_H class Empleado { protected : float sueldoFijo ; public : void a si gn ar S ue ld o ( f l o a t ) ; float sueldoBase ( ) const ; }; #endifProgramación Avanzada - Tema 3: Programación orientada a objetos – 23
  24. 24. 3.5 Miembros protegidos en la clase base (III) ® Ahora, sueldoFijo es accesible desde las clases derivadas: mecanico.cpp (versión 1 bis) ... f l o a t Mecanico : : sueldo ( ) const { f l o a t complementos = numPiezas ∗ p r e c i o P o r P i e z a ; r e t u r n s u e l d o F i j o + complementos ; } ® ... aunque sigue siendo inaccesible desde el exterior: test.cpp (versión 1 bis) i n t main ( ) { ... Mecanico m; ... c o u t m. s u e l d o F i j o ; / / i n c o r r e c t o }Programación Avanzada - Tema 3: Programación orientada a objetos – 24
  25. 25. 3.6 Redefinición de miembros de la clase base ® La función sueldoBase() de la clase Empleado y las funciones sueldo() de las Empleado sueldoFijo subclases tienen el mismo asignarSueldo(s) objetivo: devolver el sueldo sueldoBase() sueldo() de una persona. No hay ninguna razón para que tengan nombres diferentes. Mecanico Comercial numPiezas ventas ® Si usamos el mismo precioPorPieza asignarNumPrecio(n, p) asignarVentas(v) nombre, conseguiremos sueldo() sueldo() uniformizar la interfaz de las (Versión 2) tres clases, facilitando su uso.Programación Avanzada - Tema 3: Programación orientada a objetos – 25
  26. 26. 3.6 Redefinición de miembros de la clase base (II) ® Partiendo de la versión 1 (en la que no hay miembros protegidos), cambiamos el nombre de la función sueldoBase() por sueldo() en la clase base: empleado.h (versión 2) class Empleado { private : float sueldoFijo ; public : void a si gn ar S ue ld o ( f l o a t ) ; float sueldo ( ) const ; }; empleado.cpp (versión 2) ... f l o a t Empleado : : sueldo ( ) const { return sue ldoFij o ; }Programación Avanzada - Tema 3: Programación orientada a objetos – 26
  27. 27. 3.6 Redefinición de miembros de la clase base (III) ® Al implementar las subclases, redefinimos la función sueldo() con un código diferente al que tenía en la clase base. Estamos ante una forma de sobrecarga de funciones. ® Ahora la función sueldo() de las subclases debe llamar a la función sueldo() de la clase base, pero como tiene el mismo nombre, hay que utilizar el operador de resolución de ámbito: Empleado::sueldo(). mecanico.cpp (versión 2) ... f l o a t Mecanico : : sueldo ( ) const { f l o a t complementos = numPiezas ∗ p r e c i o P o r P i e z a ; r e t u r n Empleado : : sueldo ( ) + complementos ; } µ El mismo cambio lo realizamos también en la clase Comercial.Programación Avanzada - Tema 3: Programación orientada a objetos – 27
  28. 28. 3.6 Redefinición de miembros de la clase base (IV) test.cpp (versión 2) # include iostream # include empleado.h # include mecanico.h # include comercial.h using namespace s t d ; void f ( Empleado e ) { c o u t Desde f() e.sueldo() = e . sueldo ( ) e n d l ; } i n t main ( ) { Empleado e ; e . as ig na r Su el d o ( 1 0 0 0 ) ; c o u t Sueldo del empleado: e . sueldo ( ) e n d l ; Mecanico m; m. a s i gn a r S ue l do ( 2 0 0 0 ) ; m. asignarNumPrecio ( 1 0 0 , 3 ) ; c o u t Sueldo del mecánico: m. sueldo ( ) e n d l ;Programación Avanzada - Tema 3: Programación orientada a objetos – 28
  29. 29. 3.6 Redefinición de miembros de la clase base (IV) Comercial c ; c . as ig na r Su el d o ( 3 0 0 0 ) ; c . as ig na r Ve nt a s ( 2 0 0 0 ) ; c o u t Sueldo del comercial: c . sueldo ( ) e n d l ; f (e) ; f (m) ; f (c) ; } salida (versión 2) Sueldo d e l empleado : 1000 Sueldo d e l mecánico : 2300 Sueldo d e l c o m e r c i a l : 3 2 0 0 Desde f ( ) e . sueldo ( ) = 1 0 0 0 Desde f ( ) e . sueldo ( ) = 2 0 0 0 Desde f ( ) e . sueldo ( ) = 3 0 0 0Programación Avanzada - Tema 3: Programación orientada a objetos – 29
  30. 30. 3.6 Redefinición de miembros de la clase base (V) ® En el archivo test.cpp podemos apreciar la uniformidad de la interfaz de las clases: calculamos el sueldo de la misma forma para objetos de los tipos Empleado, Mecanico y Comercial. ® Observa que cuando un objeto de una clase derivada invoca a la función sueldo(), el compilador elige la versión local, es decir, la de la clase derivada y no la de la clase base. µ Decimos que la función Mecanico::sueldo() oculta la función Empleado::sueldo(). i n t main ( ) { Mecanico m; ... m. sueldo ( ) ; / / siempre se e j e c u t a l a f u n c i ó n de l a c l a s e Mecanico / / y nunca l a de l a c l a s e Empleado }Programación Avanzada - Tema 3: Programación orientada a objetos – 30
  31. 31. 3.7 Herencia y constructores ® Las clases que intervienen en una relación de herencia pueden contener constructores y destructores. ® Al declarar un objeto de una clase derivada, se ejecuta de forma automática: µ Primero: el constructor de la clase base. µ Segundo: el constructor de la clase derivada. ® Cuando un objeto de una clase derivada deja de existir (si es estático, porque el ámbito donde fue declarado ha acabado, y si es dinámico, porque se evalúa el operador delete) se ejecuta de forma automática: µ Primero: el destructor de la clase derivada. µ Segundo: el destructor de la clase base. ® Si el constructor de la clase base necesita argumentos para poder ejecutarse éstos deben ser facilitados por el constructor de la clase derivada a través de una lista de inicialización.Programación Avanzada - Tema 3: Programación orientada a objetos – 31
  32. 32. 3.7 Herencia y constructores (II) ® Partiendo de la versión 2, añadimos constructores a nuestras clases. ® Aprovechamos la ocasión para Empleado nombre incorporar a la clase base el nombre sueldoFijo Empleado(n, s) del empleado y el operador de salida. sueldo() operator(canal, emp) ® Para simplificar el código, eliminamos las funciones Mecanico Comercial Empleado::asignarSueldo(), numPiezas ventas precioPorPieza Mecanico::asignarNumPrecio() Mecanico(n, s, num, pre) Comercial(n, s, v) sueldo() sueldo() y Comercial::asignarVentas(). (Versión 3)Programación Avanzada - Tema 3: Programación orientada a objetos – 32
  33. 33. 3.7 Herencia y constructores (III) empleado.h (versión 3) # i f n d e f EMPLEADO_H # define EMPLEADO_H # include iostream # include s t r i n g using namespace s t d ; class Empleado { private : s t r i n g nombre ; float sueldoFijo ; public : Empleado ( s t r i n g , f l o a t ) ; f l o a t sueldo ( ) const ; f r i e n d ostream operator ( ostream , Empleado ) ; }; #endifProgramación Avanzada - Tema 3: Programación orientada a objetos – 33
  34. 34. 3.7 Herencia y constructores (IV) empleado.cpp (versión 3) # include empleado.h # include iostream # include s t r i n g using namespace s t d ; Empleado : : Empleado ( s t r i n g n , f l o a t s ) : nombre ( n ) , s u e l d o F i j o ( s ) { } f l o a t Empleado : : sueldo ( ) const { return sue ldoFi jo ; } ostream operator ( ostream canal , Empleado e ) { c a n a l e . nombre ; return canal ; }Programación Avanzada - Tema 3: Programación orientada a objetos – 34
  35. 35. 3.7 Herencia y constructores (V) mecanico.h (versión 3) # i f n d e f MECANICO_H # define MECANICO_H # include empleado.h # include s t r i n g using namespace s t d ; class Mecanico : public Empleado { private : int numPiezas ; f l o a t precioPorPieza ; public : Mecanico ( s t r i n g , f l o a t , i n t , f l o a t ) ; f l o a t sueldo ( ) const ; / / o c u l t a Empleado : : sueldo ( ) }; #endifProgramación Avanzada - Tema 3: Programación orientada a objetos – 35
  36. 36. 3.7 Herencia y constructores (VI) mecanico.cpp (versión 3) # include empleado.h # include mecanico.h # include s t r i n g using namespace s t d ; Mecanico : : Mecanico ( s t r i n g n , f l o a t s , i n t piezas , f l o a t p r e c i o ) : Empleado ( n , s ) , numPiezas ( p i e z a s ) , p r e c i o P o r P i e z a ( p r e c i o ) { } f l o a t Mecanico : : sueldo ( ) const { f l o a t complementos = numPiezas ∗ p r e c i o P o r P i e z a ; r e t u r n Empleado : : sueldo ( ) + complementos ; }Programación Avanzada - Tema 3: Programación orientada a objetos – 36
  37. 37. 3.7 Herencia y constructores (VII) comercial.h (versión 3) # i f n d e f COMERCIAL_H # define COMERCIAL_H # include empleado.h # include s t r i n g using namespace s t d ; class Comercial : public Empleado { private : f l o a t ventas ; public : Comercial ( s t r i n g , f l o a t , f l o a t ) ; f l o a t sueldo ( ) const ; / / o c u l t a Empleado : : sueldo ( ) }; #endifProgramación Avanzada - Tema 3: Programación orientada a objetos – 37
  38. 38. 3.7 Herencia y constructores (VIII) comercial.cpp (versión 3) # include comercial.h # include empleado.h # include s t r i n g using namespace s t d ; Comercial : : Comercial ( s t r i n g n , f l o a t s , f l o a t v ) : Empleado ( n , s ) , ventas ( v ) { } f l o a t Comercial : : sueldo ( ) const { f l o a t complementos = ventas ∗ 0 . 1 0 ; r e t u r n Empleado : : sueldo ( ) + complementos ; }Programación Avanzada - Tema 3: Programación orientada a objetos – 38
  39. 39. 3.7 Herencia y constructores (IX) test.cpp (versión 3) # include empleado.h # include mecanico.h # include comercial.h # include iostream using namespace s t d ; void f ( Empleado e ) { c o u t Desde f(): ; c o u t Sueldo de e : e . sueldo ( ) e n d l ; } i n t main ( ) { Empleado e ( Maria , 1 0 0 0 ) ; c o u t Sueldo de e : e . sueldo ( ) e n d l ; Mecanico m( Ramon , 2 0 0 0 , 1 0 0 , 3 ) ; c o u t Sueldo de m : m. sueldo ( ) e n d l ;Programación Avanzada - Tema 3: Programación orientada a objetos – 39
  40. 40. 3.7 Herencia y constructores (IX) Comercial c ( Luisa , 3 0 0 0 , 2 0 0 0 ) ; c o u t Sueldo de c : c . sueldo ( ) e n d l ; f (e) ; f (m) ; f (c) ; } salida (versión 3) Sueldo de Maria : 1000 Sueldo de Ramon : 2300 Sueldo de L u i s a : 3200 Desde f ( ) : Sueldo de Maria : 1000 Desde f ( ) : Sueldo de Ramon : 2000 Desde f ( ) : Sueldo de L u i s a : 3000Programación Avanzada - Tema 3: Programación orientada a objetos – 40
  41. 41. 3.7 Herencia y constructores (X) Observa que: ® Los constructores de las clases derivadas utilizan la lista de inicialización para pasar los argumentos que necesita el constructor de la clase base. Recuerda que primero se ejecuta automáticamente el constructor de la clase base y luego el de la derivada.Programación Avanzada - Tema 3: Programación orientada a objetos – 41
  42. 42. 3.8 Ligadura estática vs. dinámica ® Hemos pasado por alto un detalle importante: ¿por qué la salida de la ejecución del programa test.cpp muestra dos sueldos diferentes para Ramón y para Luisa? µ Cuando se calcula el sueldo de Ramón en la función main() con m.sueldo(), la función que se ejecuta es la de la clase Mecanico, puesto que m está declarado como un objeto de esa clase. µ Cuando se calcula el sueldo de Ramón en la función f() con e.sueldo() la función que se ejecuta es la de la clase Empleado, puesto que e está declarado como un objeto de esa clase, a pesar de que en ejecución el parámetro e recibe un objeto de la clase Mecanico. ® El problema radica en cómo decide el compilador qué función debe ejecutarse, es decir, cómo se liga la llamada de una función a su ejecución.Programación Avanzada - Tema 3: Programación orientada a objetos – 42
  43. 43. 3.8 Ligadura estática vs. dinámica (II) ® Existen dos tipos de ligadura: µ Ligadura estática: se produce cuando la llamada a una función de un objeto es evaluada según el tipo asociado explícitamente en la declaración del mismo. ª Esta es la ligadura que se utiliza por omisión en C y C++ y es la causa del comportamiento detectado en el programa test.cpp. µ Ligadura dinámica: se produce cuando la llamada a una función de un objeto es evaluada según el tipo asociado al objeto en tiempo de ejecución. Se conoce también como polimorfismo en tiempo de ejecución. ª En C++ este tipo de ligadura es empleada al llamar a cierto tipo de funciones especiales, conocidas como funciones virtuales, solamente cuando la llamada se hace a través de punteros o referencias. En la siguiente sección estudiaremos un caso práctico en el que la ligadura dinámica juega un papel fundamental.Programación Avanzada - Tema 3: Programación orientada a objetos – 43
  44. 44. 3.9 Funciones virtuales ® Deseamos que en nuestra jerarquía de clases Empleado, Mecanico y Comercial la función sueldo() sea Empleado invocada desde la función f() nombre sueldoFijo utilizando ligadura dinámica. Para Empleado(n, s) virtual sueldo() ello debemos: operator(canal, emp) 1. Declarar como virtual la función Mecanico Comercial sueldo() en la clase base (sólo es numPiezas ventas precioPorPieza necesario hacerlo en la clase Mecanico(n, s, num, pre) Comercial(n, s, v) sueldo() sueldo() base). 2. Modificar la implementación de f() (Versión 4) para que la llamada a la función sueldo() se realice a través de un puntero o una referencia.Programación Avanzada - Tema 3: Programación orientada a objetos – 44
  45. 45. 3.9.1 Invocación de funciones virtuales a través de un puntero empleado.h (versión 4) class Empleado { ... v i r t u a l f l o a t sueldo ( ) const ; }; test.cpp (versión 4) void f ( Empleado ∗ p t r _ e ) { c o u t Desde f(): ; c o u t Sueldo de ( ∗ p t r _ e ) : p t r _ e −sueldo ( ) e n d l ; } i n t main ( ) { ... f (e ) ; f (m) ; f ( c ) ; }Programación Avanzada - Tema 3: Programación orientada a objetos – 45
  46. 46. 3.9.1 Invocación de funciones virtuales a través de un puntero (II) salida (versión 4) Sueldo de Maria : 1000 Sueldo de Ramon : 2300 Sueldo de L u i s a : 3200 Desde f ( ) : Sueldo de Maria : 1000 Desde f ( ) : Sueldo de Ramon : 2300 Desde f ( ) : Sueldo de L u i s a : 3200 ® La función f() recibe como argumento un puntero a un objeto de la clase Empleado y a través de él llama a la función sueldo(). ® Ante esta situación, el compilador retrasa la decisión de qué función sueldo() ejecutar hasta el momento de la ejecución. ® En cada una de las tres llamadas a la función f() se recibe como argumento un puntero a un objeto de tipo Empleado, Mecanico y Comercial respectivamente. En cada caso, ptr_e-sueldo() invoca la función sueldo() de la clase correspondiente.Programación Avanzada - Tema 3: Programación orientada a objetos – 46
  47. 47. 3.9.2 Invocación de funciones virtuales a través de una referencia empleado.h (versión 5) class Empleado { ... v i r t u a l f l o a t sueldo ( ) const ; }; test.cpp (versión 5) void f ( Empleado e ) { c o u t Desde f(): ; c o u t Sueldo de e : e . sueldo ( ) e n d l ; } i n t main ( ) { ... f (e) ; f (m) ; f (c) ; }Programación Avanzada - Tema 3: Programación orientada a objetos – 47
  48. 48. 3.9.2 Invocación de funciones virtuales a través de una referencia (II) salida (versión 5) Sueldo de Maria : 1000 Sueldo de Ramon : 2300 Sueldo de L u i s a : 3200 Desde f ( ) : Sueldo de Maria : 1000 Desde f ( ) : Sueldo de Ramon : 2300 Desde f ( ) : Sueldo de L u i s a : 3200 ® La función f() recibe como argumento una referencia a un objeto de la clase Empleado y a través de ella llama a la función sueldo(). ® Ante esta situación, el compilador retrasa la decisión de qué función sueldo() ejecutar hasta el momento de la ejecución. ® En cada una de las tres llamadas a la función f() se recibe como argumento un objeto de tipo Empleado, Mecanico y Comercial respectivamente. En cada caso, e.sueldo() invoca la función sueldo() de la clase correspondiente.Programación Avanzada - Tema 3: Programación orientada a objetos – 48
  49. 49. 4 Caso práctico: aplicación gráfica Como ejemplo de aplicación de la ligadura dinámica (o polimorfismo en tiempo de ejecución) estudiaremos un caso práctico: una aplicación gráfica. ® Diseño de la jerarquía de clases. ® Ligadura estática. ® Ligadura dinámica y polimorfismo. ® Clases abstractas.Programación Avanzada - Tema 3: Programación orientada a objetos – 49
  50. 50. 4.1 Identificación de objetos ® La aplicación gráfica debe manipular una serie de figuras como cuadrados, rectángulos, círculos, elipses, etc. Todos estos objetos se caracterizan por ciertos rasgos comunes: µ Atributos (datos) ª Coordenadas ª Color del fondo ª Color del borde ª Grosor del trazo µ Operaciones (funciones) ª Mover ª Dibujar ª Área ª PerímetroProgramación Avanzada - Tema 3: Programación orientada a objetos – 50
  51. 51. 4.2 Diseño de la jerarquía de clases Figura x, y, fondo, borde mover, dibujar, área, perímetro Elipse Rectángulo Triángulo ejeMayor, ejeMenor base, altura vértices dibujar, dibujar, dibujar, área, perímetro área, perímetro área, perímetro Círculo Cuadrado Notación Booch radio A B área, perímetro área, perímetro Clase A hereda de clase BProgramación Avanzada - Tema 3: Programación orientada a objetos – 51
  52. 52. 4.3 Diseño de la clase base class F i g u r a { Punto p o s i c i o n ; C o l o r fondo , borde ; // ... Notación Booch public : A B F i g u r a ( Punto p , C o l o r c1 , C o l o r c2 ) ; Clase A contiene void mover ( Punto p) ; una instancia de clase B void d i b u j a r ( ) const ; f l o a t area ( ) const ; 1 1 Punto f l o a t p e r i m e t r o ( ) const ; // ... Figura 1 }; 2 void F i g u r a : : mover ( Punto p ) { Color posicion = p ; }Programación Avanzada - Tema 3: Programación orientada a objetos – 52
  53. 53. 4.4 Diseño de las subclases class E l i p s e : public F i g u r a { f l o a t ejeMenor , ejeMayor ; public : Elipse ( float a , float b) ; void d i b u j a r ( ) const ; f l o a t area ( ) const ; f l o a t p e r i m e t r o ( ) const ; }; f l o a t E l i p s e : : area ( ) const { r e t u r n p i ∗ ejeMenor ∗ ejeMayor ; } void E l i p s e : : d i b u j a r ( ) const { // ... }Programación Avanzada - Tema 3: Programación orientada a objetos – 53
  54. 54. 4.4 Diseño de las subclases (II) class Rectangulo : public F i g u r a { f l o a t base , a l t u r a ; public : Rectangulo ( f l o a t a , f l o a t b ) ; void d i b u j a r ( ) const ; f l o a t area ( ) const ; f l o a t p e r i m e t r o ( ) const ; }; f l o a t Rectangulo : : area ( ) const { r e t u r n base ∗ a l t u r a ; } void Rectangulo : : d i b u j a r ( ) const { // ... }Programación Avanzada - Tema 3: Programación orientada a objetos – 54
  55. 55. 4.5 Estructura de datos heterogénea i n t main ( ) { 1 F i g u r a ∗ v [N ] ; 0 v [ 0 ] = new Rectangulo ( . . . ) ; v [ 1 ] = new E l i p s e ( . . . ) ; 2 v [ 2 ] = new C i r c u l o ( . . . ) ; v [ 3 ] = new Cuadrado ( . . . ) ; 3 // ... d i b u j a r ( v ) ; / / d i b u j a todas l a s f i g u r a s f l o a t area = a r e a T o t a l ( v ) ; / / suma de l a s áreas unRectángulo 0 } v[0] atributos v[1] unaElipse 1 Definimos una estructura de datos heterogénea atributos v[2] (un simple vector de punteros) que nos permita v[3] unCírculo 2 atributos almacenar objetos de tipos diferentes. Recuerda … … unCuadrado 3 que los punteros a objetos de una clase base atributos pueden apuntar a objetos de cualquier clase derivada.Programación Avanzada - Tema 3: Programación orientada a objetos – 55
  56. 56. 4.6 Operaciones globales void d i b u j a r ( F i g u r a ∗ v [ ] ) { 1 f o r ( i n t i = 0 ; i N ; i ++) 0 v [ i ]− d i b u j a r ( ) ; } 2 f l o a t areaTotal ( Figura ∗ v [ ] ) { 3 f l o a t a t =0; f o r ( i n t i = 0 ; i N ; i ++) a t = a t + v [ i ]−area ( ) ; unRectángulo 0 return at ; v[0] atributos } v[1] unaElipse 1 atributos v[2] Se pretende que estas dos operaciones v[3] unCírculo 2 atributos globales operen sobre el conjunto de figuras … … unCuadrado 3 almacenadas en la estructura de datos. ¿Lo atributos conseguimos realmente? Observemos la llamada v[i]-area()...Programación Avanzada - Tema 3: Programación orientada a objetos – 56
  57. 57. 4.7 Ligadura estática ® Ligadura estática: Se produce cuando la invocación a un método de un objeto es evaluada según el tipo T asociado explícitamente con su nombre en la declaración del mismo. La llamada v[1]-area() invoca la función Figura::area() porque v[1] está declarado como puntero a Figura. f l o a t areaTotal ( Figura ∗ v [ ] ) { f l o a t a t =0; f o r ( i n t i = 0 ; i N ; i ++) a t = a t + v [ i ]−area ( ) ; return at ; } unaElipse unaElipse atributos/métodos v[1] atributos de Figura atributos/métodos de Elipse Figura * Figura v[1]-area( ) Figura::area( )Programación Avanzada - Tema 3: Programación orientada a objetos – 57
  58. 58. 4.8 Funciones virtuales ® La palabra reservada virtual permite retrasar la decisión de qué método debe invocarse hasta el momento de la ejecución. class F i g u r a { Punto p o s i c i o n ; C o l o r fondo , borde ; // ... public : F i g u r a ( Punto p , C o l o r c1 , C o l o r c2 ) ; void mover ( Punto p) ; v i r t u a l void d i b u j a r ( ) const ; v i r t u a l f l o a t area ( ) const ; v i r t u a l f l o a t p e r i m e t r o ( ) const ; // ... };Programación Avanzada - Tema 3: Programación orientada a objetos – 58
  59. 59. 4.9 Ligadura dinámica ® Ligadura dinámica: Se produce cuando la invocación a un método de un objeto es evaluada según el tipo T asociado al objeto en tiempo de ejecución. En C++ se utiliza al invocar funciones virtuales a través de punteros o referencias. La llamada v[1]-area() invoca la función Elipse::area() porque v[1] apunta en tiempo de ejecución a una Elipse. f l o a t a r e a T o t a l ( F i g u r a ∗ v [ ] ) { / / s i n cambios r e s p e c t o a l a v e r s i ó n a n t e r i o r f l o a t a t =0; f o r ( i n t i = 0 ; i N ; i ++) a t = a t + v [ i ]−area ( ) ; return at ; } unaElipse unaElipse atributos/métodos v[1] atributos de Figura atributos/métodos Figura * Figura de Elipse v[1]-area( ) Elipse::area( )Programación Avanzada - Tema 3: Programación orientada a objetos – 59
  60. 60. 4.9 Ligadura dinámica (II) ® Añadimos la subclase PoligonoLibre a la jerarquía:Programación Avanzada - Tema 3: Programación orientada a objetos – 60
  61. 61. 4.9 Ligadura dinámica (III) ® La función areaTotal() sigue siendo válida para la nueva figura: i n t main ( ) { F i g u r a ∗ v [N ] ; // ... unPoligonoLibre v [ 3 ] = new Cuadrado ( . . . ) ; unPoligonoLibre atributos/métodos v[4] v [ 4 ] = new P o l i g o n o L i b r e ( . . . ) ; atributos de Figura // ... atributos/métodos Figura * Figura de Elipse f l o a t area= a r e a T o t a l ( v ) ; v[4]-area( ) PoligonoLibre::area( ) f l o a t areaTotal ( Figura ∗ v [ ] ) { f l o a t a t =0; f o r ( i n t i = 0 ; i N ; i ++) a t = a t + v [ i ]−area ( ) ; return at ; }Programación Avanzada - Tema 3: Programación orientada a objetos – 61
  62. 62. 4.10 Clases abstractas ® La clase Figura constituye una interfaz común a toda la jerarquía y contiene métodos cuya implementación sólo tiene sentido en las clases derivadas: las funciones virtuales puras. ® Una clase base con funciones virtuales puras se convierte en clase abstracta. Tal clase no puede tener instancias. class F i g u r a { // ... public : F i g u r a ( Punto p , C o l o r c1 , C o l o r c2 ) ; void mover ( Punto p) ; v i r t u a l void d i b u j a r ( ) const = 0 ; v i r t u a l f l o a t area ( ) const = 0 ; v i r t u a l f l o a t p e r i m e t r o ( ) const = 0 ; // ... };Programación Avanzada - Tema 3: Programación orientada a objetos – 62
  63. 63. 4.11 Conclusiones del caso práctico ® La herencia puede usarse para especificar una interfaz común para un grupo de clases derivadas. ® La clase base, al especificar el prototipo de las funciones, define las operaciones y características requeridas por las clases derivadas y son implementadas por estas últimas. ® Esta técnica permite el diseño de programas que operan sobre objetos diferentes de una manera genérica: nos encontramos ante un tipo de polimorfismo. ® En C++ este comportamiento polimórfico se obtiene usando herencia y funciones virtuales. ® Este mecanismo es esencial a la POO y constituye la diferencia fundamental con respecto a la programación basada en objetos.Programación Avanzada - Tema 3: Programación orientada a objetos – 63
  64. 64. Fin Copyright c 2004 José Luis Llopis BorrásRealizada con ujislides c 2002-3 Sergio Barrachina (barrachi@icc.uji.es)

×