En la programación orientada a objetos (POO), la vida útil de un objeto es el período de tiempo entre su creación y su destrucción. En algunos contextos de programación, la vida útil del objeto coincide con la vida útil de la variable que lo representa. En otros contextos , donde se accede al objeto por referencia , la vida útil del objeto no está determinada por la vida útil de la variable. Por ejemplo, la destrucción de la variable puede destruir únicamente la referencia, no el objeto referenciado. [ 1 ]
Determinismo
La creación de un objeto suele ser determinista , pero su destrucción varía según el contexto de programación. Algunos contextos permiten la destrucción determinista, mientras que otros no. En particular, en un entorno con recolección de basura, los objetos se destruyen cuando el recolector de basura lo decide.
La sintaxis para la creación y destrucción varía según el contexto de programación. En muchos contextos, incluidos C++, C# y Java, un objeto se crea mediante una sintaxis especial como . En C++, que proporciona gestión manual de memoria , un objeto se destruye mediante la palabra clave . En C# y Java, sin una sintaxis de destrucción explícita, el recolector de basura destruye automáticamente los objetos no utilizados. [ 1 ]new typename()delete
Un enfoque alternativo y determinista para la destrucción automática consiste en que el objeto se destruya cuando el código decrementa el contador de referencias del objeto a cero. [ 2 ]
En un repositorio de objetos , donde estos pueden crearse con antelación y reutilizarse, la creación y destrucción aparente de un objeto puede no corresponderse con la real. El repositorio proporciona la reinicialización para la creación y la finalización para la destrucción. Tanto la creación como la destrucción pueden ser no deterministas.
Los objetos con asignación de memoria estática tienen una vida útil que coincide con la ejecución de un programa, pero el orden de creación y destrucción de los distintos objetos estáticos generalmente no es determinista.
Ciclo vital
El ciclo de vida de un objeto se refiere a los eventos que experimenta un objeto, incluyendo y entre la creación y la destrucción. [ 3 ] [ 4 ]
El ciclo de vida generalmente incluye la gestión de memoria y las operaciones posteriores a la asignación y previas a la liberación. La creación de objetos generalmente consiste en la asignación de memoria y la inicialización , donde la inicialización incluye la asignación de valores a los campos y la ejecución del código de inicialización. La destrucción de objetos generalmente consiste en la finalización (también conocida como limpieza) y la liberación de memoria . Estos pasos generalmente se suceden en el siguiente orden: asignar, inicializar, finalizar, liberar.
En función del contexto de programación, estos pasos pueden automatizarse parcial o totalmente, y algunos de ellos pueden personalizarse.
La frecuencia de personalización suele variar según el paso y el contexto de programación. La inicialización es el paso que más se personaliza. La finalización es común en lenguajes con destrucción determinista, especialmente C++, pero rara en lenguajes con recolección de basura. La asignación rara vez se personaliza, y la desasignación generalmente no se puede personalizar.
Creación
La forma de crear objetos varía según el contexto de programación. En algunos lenguajes basados en clases, un constructor se encarga de la inicialización. Al igual que otros métodos, un constructor puede sobrecargarse para admitir la creación de objetos con diferentes estados iniciales.
Destrucción
Por lo general, un objeto se elimina de la memoria cuando ya no es necesario. Sin embargo, si hay suficiente memoria o el programa tiene un tiempo de ejecución corto, es posible que no se destruya el objeto, sino que la memoria simplemente se libere al finalizar el proceso.
En algunos casos, la destrucción de un objeto consiste únicamente en liberar memoria, especialmente con la recolección de basura, o si el objeto es una estructura de datos simple . En otros casos, se realiza una limpieza previa a la liberación de memoria, en particular destruyendo objetos miembros (en la gestión manual de memoria) o eliminando referencias del objeto a otros objetos para disminuir los contadores de referencias (en el conteo de referencias). Esto puede ser automático o se puede invocar un método de destrucción especial sobre el objeto.
En lenguajes basados en clases con ciclo de vida determinista de los objetos, especialmente C++, se llama a un destructor cuando se elimina una instancia, antes de que se libere la memoria. [ 1 ] En C++, los destructores difieren de los constructores en varios aspectos. No se pueden sobrecargar, no deben tener argumentos, no necesitan mantener invariantes de clase y pueden provocar la terminación del programa si lanzan excepciones.
Con la recolección de basura , los objetos pueden ser destruidos cuando el programa ya no puede acceder a ellos. El recolector de basura llama a un finalizador antes de la liberación de memoria. [ 2 ]
Destruir un objeto invalida cualquier referencia a él. Con la gestión manual de memoria, cualquier referencia existente se convierte en una referencia colgante . Con la recolección de basura, los objetos solo se destruyen cuando no hay referencias a ellos.
Consistencia
El ciclo de vida de un objeto comienza cuando se completa la asignación y termina cuando comienza la desasignación. Por lo tanto, durante la inicialización y la finalización, un objeto está activo, pero puede que no se encuentre en un estado consistente. El período comprendido entre la finalización de la inicialización y el inicio de la finalización es cuando el objeto está activo y en un estado consistente.
Si la creación o la destrucción fallan, la notificación de errores (por ejemplo, el lanzamiento de una excepción) puede resultar complicada, ya que el objeto o los objetos relacionados pueden encontrarse en un estado inconsistente.
Para una variable estática , cuya duración coincide con la ejecución del programa, un error de creación o destrucción resulta problemático, ya que la ejecución del programa se produce antes o después de la ejecución normal.
Programación basada en clases
En la programación basada en clases, la creación de objetos también se conoce como instanciación (crear una instancia de una clase ). La creación y la destrucción se pueden personalizar mediante un constructor y un destructor , y a veces con métodos de inicialización y finalización separados .
Cabe destacar que un constructor es un método de clase, ya que no hay ningún objeto (instancia) disponible hasta que se crea el objeto, pero los destructores, inicializadores y finalizadores son métodos de instancia.
Además, los constructores e inicializadores a menudo pueden aceptar argumentos, mientras que los destructores y finalizadores generalmente no, ya que suelen ser invocables implícitamente.
Gestión de recursos
En lenguajes con objetos de tiempo de vida determinista, el tiempo de vida puede usarse para aprovechar la gestión de recursos . Esto se conoce como el modismo de Adquisición de Recursos durante la Inicialización (RAII). Los recursos se adquieren durante la inicialización y se liberan durante la finalización. [ 4 ] [ 5 ] En lenguajes con objetos de tiempo de vida no determinista (es decir, con recolección de basura), la gestión de la memoria generalmente se mantiene separada de la gestión de otros recursos.
Ejemplos
C++
Una clase de C++ se puede declarar con valores predeterminados como: [ 4 ]
clase Foo { // ... };Cuando se declara en un contexto automático, el objeto se destruye al finalizar el bloque en el que se declara. [ 4 ]
int bar () { Foo foo ; // foo se destruye aquí }Cuando se crea dinámicamente, existe hasta que se destruye explícitamente. [ 4 ]
int bar () { Foo * foo = new Foo (); delete foo ; }Al utilizar punteros inteligentes , su semántica de destrucción se controla en función del ámbito o de las referencias compartidas.
Java
Una clase Java se puede declarar con valores predeterminados de la siguiente manera:
clase Foo { // ... }Después de que se crea una instancia (es decir, new Foo()), esta existe hasta que no tiene referencias y el recolector de basura la elimina. [ 6 ]
Óxido
Rust utiliza anotaciones de tiempo de vida para declarar el tiempo de vida de los argumentos pasados a una función. El tiempo de vida se declara en la firma de la función entre corchetes angulares. En este ejemplo, se pasa una referencia a la estructura File a la función de ejemplo con la anotación de tiempo de vida 'a . [ 7 ]
fn example <' a > ( archivo : & ' a Archivo ) { // ... }Referencias
- 1 2 3 Scott, Michael (23 de marzo de 2009). Pragmática de los lenguajes de programación (3.ª ed.). Morgan Kaufmann . págs. 114–115 , 120–121 , 356–359 . ISBN 978-0-08-092299-7.
- 1 2 Jones, Richard; Hosking, Antony; Moss, Eliot (1 de junio de 2023). The Garbage Collection Handbook: The Art of Automatic Memory Management (2.ª ed.). CRC Press . págs. 61, 312. doi : 10.1201/9781003276142 . ISBN 978-1-000-88368-8.
- ↑ Campbell, Matthew (22 de septiembre de 2012). Objective-C Recipes: A Problem-Solution Approach . Apress. p. 262. ISBN 978-1-4302-4372-4.
- 1 2 3 4 5 Gregoire, Marc (24 de febrero de 2021). Professional C++ . John Wiley & Sons . pp. 41– 45, 211– 213, 258– 259, 1091– 1093. ISBN 978-1-119-69540-0.
- ↑ Mallia, Antonio; Zoffoli, Francesco (15 de marzo de 2019). Fundamentos de C++: Domina C++, el lenguaje que da soporte a los gigantes tecnológicos a nivel mundial . Packt Publishing Ltd. ISBN 978-1-78980-390-7.
- ↑ Schildt, Herbert (12 de noviembre de 2021). Java: The Complete Reference (12.ª ed.). McGraw-Hill . ISBN 978-1-260-46342-2.
- ↑ Gjengset, Jon (21 de diciembre de 2021). Rust para Rustaceans: Programación idiomática para desarrolladores experimentados . No Starch Press . págs. 14–15 . ISBN 978-1-7185-0186-7.
- Objeto (informática)
