Inicialización y Destrucción de Objetos Asociación Dinámica de Métodos Lenguajes de Programación I Inicialización y Destrucción - Asociación Dinámica de Métodos Ernesto Hernández-Novich <emhn@usb.ve> Copyright (c) 2007 Inicialización y Destrucción de Objetos Asociación Dinámica de Métodos Inicialización de Objetos En los lenguajes orientados a objetos existe un mecanismo especial para inicializar los objetos. Son subrutinas denominadas constructores. Se ejecutan automáticamente a tiempo de elaboración del objeto. No reservan espacio, sólo lo inicializan. En algunos lenguajes es necesario contar con un sistema similar para finalizar los objetos. Son subrutinas denominadas destructores. Se ejecutan automáticamente a tiempo de finalización del objeto. Generalmente sirven para liberar espacio. Inicialización y Destrucción de Objetos Asociación Dinámica de Métodos ¿Cómo escoger el constructor? Existen lenguajes que permiten más de un constructor. Todos con el mismo nombre, siendo necesario seleccionar por la firma de los argumentos (C++, Java, C++). Con nombres diferentes (Smalltalk, Eiffel). Existen lenguajes que no permiten constructores (Modula-3). Existen lenguajes que sólo permiten constructores si las clases tienen propiedades especiales (Ada). Inicialización y Destrucción de Objetos Asociación Dinámica de Métodos Modelo de Referencia vs. Modelo de Valores Si el lenguaje usa referencias a objetos (Java, Ruby, Pyhton, Simula) Cada objeto es creado de forma explícita. Es fácil asegurarse de la ejecución de los constructores. Si el lenguaje usa valores que son objetos (C++, Modula-3, Ada) Los objetos pueden aparecer de forma implícita a tiempo de elaboración. Es crucial que los constructores sean ejecutados. Inicialización y Destrucción de Objetos Asociación Dinámica de Métodos Dificultades del modelo de valores El ejemplo de C++ Constructores con parámetros. foo b; // foo::foo() foo b(10,’x’); // foo:foo(int,char) Constructores para clonación foo a; bar b; foo c(a); foo d(b); // foo::foo(foo&) // foo::foo(bar&) Inicialización y Destrucción de Objetos Asociación Dinámica de Métodos Más complicaciones en C++ Todo constructor de un argumento se denomina Constructor para Copia. foo bar ... foo foo a; b; // foo::foo() // bar::bar() c = a; d = b; // foo::foo(foo&) // foo::foo(bar&) !Pero no es una asignación! foo bar ... c = d = a, c, d; // foo::foo() tres veces b; // bar::bar() a; b; // foo::operator=(foo&) // foo::operator=(bar&) Inicialización y Destrucción de Objetos Asociación Dinámica de Métodos Afectan el rendimiento Una declaración como foo a = b + c se convierte en foo t; t = b.operator+(c); foo a = t; Al pasar parámetros por valor a las subrutinas, se hace necesario un constructor de copia. Inicialización y Destrucción de Objetos Asociación Dinámica de Métodos Orden de Ejecución Si una clase B deriva de una clase A, ¿en que orden deben ejecutarse los constructores? En C++, es necesario que el constructor de A se ejecute antes que el de B. Al declarar el constructor de B, se indican los parámetros para el constructor de A B::B( B args ) : A( A args ) { También se puede indicar los argumentos para atributos class B : A { m1_t m1; m2_t m2; } B::B ( B args ) : A ( A args ), m1 ( m1 args ), m2 ( m2 args ) { Inicialización y Destrucción de Objetos Asociación Dinámica de Métodos Orden de Ejecución Java tiene la misma condición de C++, pero utiliza el modelo de referencia. La llamada al constructor de la superclase se hace de forma explícita usando super. Si no se hace, entonces el compilador incluye una llamada implícita al constructor de cero argumentos. Los atributos que sean objetos se inicializan con una referencia nula. Inicialización y Destrucción de Objetos Asociación Dinámica de Métodos Destrucción de Objetos En C++, los destructores son llamados en orden inverso de herencia. En C++, se necesitan los destructores para liberar el espacio de memoria reservado explícitamente. En lenguajes con recolección de basura, los destructores son prácticamente inútiles. Inicialización y Destrucción de Objetos Asociación Dinámica de Métodos Polimorfismo de Subtipos Si una clase B deriva a una clase A B tiene todos los atributos y métodos de A. B es estructuralmente equivalente a A. Cualquier cosa posible sobre un objeto de clase A, también es posible sobre un objeto de clase B. Excelente para programar de manera general class persona { persona::imprimir_carnet() } class estudiante : public persona { ... } class profesor : public persona { ... } estudiante e; profesor p; persona *x = &e, *y = &p; e.imprimir_carnet(); p.imprimit_carnet(); Inicialización y Destrucción de Objetos Asociación Dinámica de Métodos ¿Qué pasa al redefinir métodos? Si redefinimos imprimir_carnet en ambas subclases. e.imprimir_carnet() invoca al método de la clase estudiante. p.imprimir_carnet() invoca al método de la clase profesor. ¿A cuál método debe invocar x->imprimir_carnet()? Si la decisión depende del tipo de x se trata de asociación estática de métodos y se decide a tiempo de compilación. Si la decisión depende del tipo del objeto apuntado por x se trata de asociación dinámica de métodos y se decide a tiempo de ejecución. Inicialización y Destrucción de Objetos Asociación Dinámica de Métodos Métodos Virtuales y No Virtuales Muchos lenguajes usan asociación dinámica de métodos siempre (Python, Ruby, Perl, Smalltalk). Otros lenguajes tienen ambos métodos, pero usan el dinámico por omisión (Java, Eiffel). Para indicar que un método debe asociarse estáticamente se le agrega una palabra clave (final en Java, frozen en Eiffel). Esto impide que sean redefinidos en las subclases y puede optimizarse su invocación. Otros lenguajes prefieren el estático por omisión (C++, C#, Ada). Para indicar que un método debe asociarse dinámicamente se le agrega una palabra clave (virtual en C++). Inicialización y Destrucción de Objetos Asociación Dinámica de Métodos Clases Abstractas Se dice que un método es abstracto si se omite el cuerpo. Una Clase Abstracta es aquella que tiene al menos un método abstracto. No pueden ser instanciadas. Sólo sirven como base para clases concretas. Los métodos abstractos sirven como enlace para proveer asociación dinámica de métodos. Inicialización y Destrucción de Objetos Asociación Dinámica de Métodos Implantación de la Asociación Dinámica Todo objeto debe contener suficiente información para decidir los atributos y métodos adecuados a tiempo de ejecución. Cada objeto se representa como un registro cuyo primer campo contiene una referencia a la tabla de métodos virtuales (vtable). La vtable es un arreglo cuya i-ésima entrada apunta al segmento de código del i-ésimo método virtual del objeto. Inicialización y Destrucción de Objetos Construcción de vtables Para la clase base class foo { int a; double b; char c; public: virtual void k(... virtual int l(... virtual void m(... virtual double n(... } F; Asociación Dinámica de Métodos Inicialización y Destrucción de Objetos Construcción de vtables Para una clase derivada class bar : public foo { int w; public: void m(... virtual double s(... virtual char *t(... } B; Asociación Dinámica de Métodos Inicialización y Destrucción de Objetos Asociación Dinámica de Métodos Herencia Múltiple Si dos clases ancestrales proveen el mismo método: ¿Ambas son accesibles? ¿Cuál debe utilizarse? Si dos clases ancestrales derivan de un ancestro común: ¿Hay una o dos copias de los métodos del ancestro? ¿Cómo representar las instancias de las subclases? Interfaces vs. Herencia Múltiple