Puntero inteligente

La versión actual de la página aún no ha sido revisada por colaboradores experimentados y puede diferir significativamente de la versión revisada el 22 de marzo de 2021; las comprobaciones requieren 17 ediciones .

Un  puntero inteligente es un idioma de indirección de memoria que se usa ampliamente cuando se programa en lenguajes de alto nivel como C++ , Rust , etc. Por regla general, se implementa como una clase especializada (generalmente parametrizada ), que imita la interfaz de un puntero regular y agrega la nueva funcionalidad necesaria (por ejemplo, verificación de límites en el acceso o limpieza de memoria ) [1] .

Por lo general, el propósito principal del uso de punteros inteligentes es encapsular el manejo dinámico de la memoria de tal manera que las propiedades y el comportamiento de los punteros inteligentes imiten las propiedades y el comportamiento de los punteros regulares. Al mismo tiempo, son responsables de la liberación oportuna y precisa de los recursos asignados, lo que simplifica el desarrollo del código y el proceso de depuración, eliminando las fugas de memoria y la aparición de enlaces colgantes [2] .

Indicadores de propiedad compartida (con recuento de referencias)

Estos se usan comúnmente con objetos que tienen operaciones especiales "aumentar el recuento de referencias" ( AddRef()en COM ) y "reducir el recuento de referencias" ( Release()en COM). La mayoría de las veces, dichos objetos se heredan de una clase o interfaz especial (por ejemplo, IUnknownen COM).

Cuando aparece una nueva referencia a un objeto, se llama a la operación “aumentar el número de referencias”, y cuando se destruye, se llama a la operación “disminuir el número de referencias”. Si, como resultado de la operación "reducir referencias", el número de referencias a un objeto se vuelve cero, entonces el objeto se elimina.

Esta técnica se llama conteo automático de referencias . Hace coincidir el número de punteros que almacenan la dirección del objeto con el número de referencias almacenadas en el objeto, y cuando este número llega a cero, hace que el objeto se elimine. Sus ventajas son una confiabilidad relativamente alta, velocidad y facilidad de implementación en C++ . La desventaja es que se vuelve más difícil de usar en caso de referencias circulares (la necesidad de usar "referencias débiles").

Implementaciones

Hay dos tipos de punteros de este tipo: con almacenamiento de contador dentro del objeto y con almacenamiento de contador fuera.

La opción más simple es almacenar el contador dentro de un objeto administrado. En COM , los objetos contados por referencia se implementan de la siguiente manera:

Implementado de la misma manera boost::intrusive_ptr.

Los std::shared_ptrcontadores de referencia se almacenan fuera del objeto, en una estructura de datos especial. Este puntero inteligente tiene el doble de tamaño que uno estándar (tiene dos campos, uno apunta a la contraestructura y el segundo al objeto gestionado). Este diseño permite:

Dado que la estructura del contador es pequeña, se puede asignar, por ejemplo, a través del grupo de objetos .

El problema de las referencias circulares

Supongamos que hay dos objetos y cada uno de ellos tiene un puntero propietario. Al puntero en el primer objeto se le asigna la dirección del segundo objeto, y el puntero en el segundo es la dirección del primer objeto. Si ahora a todos los punteros externos (es decir, no almacenados dentro de estos objetos) a dos objetos dados se les asignan nuevos valores, entonces los punteros dentro de los objetos aún se poseerán entre sí y permanecerán en la memoria. Como resultado, habrá una situación en la que no se podrá acceder a los objetos, es decir, una pérdida de memoria .

El problema de las referencias circulares se resuelve ya sea mediante el diseño adecuado de las estructuras de datos, o mediante el uso de la recolección de basura , o mediante el uso de dos tipos de referencias: fuertes (propietarias) y débiles (no propietarias, por ejemplo std::weak_ptr).

Ejemplos de implementación

Marcadores de propiedad única

A menudo, los punteros de propiedad compartida son demasiado grandes y "pesados" para las tareas del programador: por ejemplo, debe crear un objeto de uno de los tipos N, poseerlo, acceder a sus funciones virtuales de vez en cuando y luego eliminarlo correctamente. Para hacer esto, use el "hermano pequeño", un indicador de propiedad exclusiva.

Dichos punteros al asignar un nuevo valor o eliminarse eliminan el objeto. La asignación de punteros de propiedad única solo es posible con la destrucción de uno de los punteros; por lo tanto, nunca habrá una situación en la que dos punteros posean el mismo objeto.

Su desventaja es la dificultad de pasar un objeto fuera del alcance del puntero.

Ejemplos de implementación

Punteros al búfer de memoria de otra persona

En la mayoría de los casos, si hay una función que se ocupa de una matriz, se escribe una de dos cosas:

clasificación vacía ( tamaño_t tamaño , int * datos ); // puntero + tamaño void sort ( std :: vector < int >& data ); // estructura de memoria especifica

El primero excluye la verificación automática de rango. El segundo limita la aplicabilidad std::vectorde 's, y no puede ordenar, por ejemplo, una cadena de una matriz o parte de otra vector's.

Por lo tanto, en las bibliotecas desarrolladas para funciones que usan los búferes de memoria de otras personas, usan tipos de datos "ligeros" como

plantilla < claseT > _ estructura Buf1d { datos T * ; tamaño_t tamaño ; Buf1d ( estándar :: vector < T > & vec ); T & operador []( tamaño_t i ); };

A menudo se usa para cadenas: analizar , ejecutar un editor de texto y otras tareas específicas necesitan sus propias estructuras de datos que son más rápidas que los métodos estándar de manipulación de cadenas.

Ejemplos de implementación

  • biblioteca de plantillas estándar: std::string_view, std::span.
  • qt: QStringView.

Notas

  1. Alger D. Punteros inteligentes como modismo // C++. Biblioteca del programador . - 1999. - S. 75. - 320 p. — ISBN 0-12-049942-8 . Archivado el 12 de julio de 2018 en Wayback Machine .
  2. Ivor Horton, Peter Van Weert. Punteros sin procesar y punteros inteligentes // A partir de C++17. De novato a profesional. - 5to. - Apress, 2018. - Pág. 206. - ISBN 978-1-4842-3365-8 .

Enlaces