stringtranslate.com

Puntero inteligente

En informática , un puntero inteligente es un tipo de datos abstracto que simula un puntero y al mismo tiempo proporciona funciones adicionales, como administración automática de memoria o verificación de límites . Estas funciones tienen como objetivo reducir los errores causados ​​por el mal uso de los punteros, al tiempo que se conserva la eficiencia. Los punteros inteligentes suelen realizar un seguimiento de la memoria a la que apuntan y también pueden usarse para administrar otros recursos, como conexiones de red y controladores de archivos . Los punteros inteligentes se popularizaron por primera vez en el lenguaje de programación C++ durante la primera mitad de la década de 1990 como refutación a las críticas sobre la falta de recolección automática de basura de C++ . [1] [2]

El uso indebido de punteros puede ser una fuente importante de errores. Los punteros inteligentes evitan la mayoría de las situaciones de fugas de memoria al hacer que la desasignación de memoria sea automática. En términos más generales, hacen que la destrucción de objetos sea automática: un objeto controlado por un puntero inteligente se destruye automáticamente ( se finaliza y luego se desasigna) cuando se destruye el último (o único) propietario de un objeto, por ejemplo, porque el propietario es una variable local y la ejecución sale del ámbito de la variable . Los punteros inteligentes también eliminan los punteros pendientes al posponer la destrucción hasta que un objeto ya no esté en uso.

Si un lenguaje admite la recolección automática de basura (por ejemplo, Java o C# ), entonces los punteros inteligentes no son necesarios para los aspectos de recuperación y seguridad de la administración de memoria, pero son útiles para otros propósitos, como la administración de la residencia de la estructura de datos de caché y la administración de recursos de objetos como identificadores de archivos o sockets de red .

Existen varios tipos de punteros inteligentes. Algunos funcionan con recuento de referencias , otros asignando la propiedad de un objeto a un puntero.

Historia

Aunque C++ popularizó el concepto de punteros inteligentes, especialmente la variedad de conteo de referencias , [3] el predecesor inmediato de uno de los lenguajes que inspiraron el diseño de C++ tenía referencias de conteo de referencias integradas en el lenguaje. C++ se inspiró en parte en Simula67 . [4] El antecesor de Simula67 fue Simula I. En la medida en que el elemento de Simula I es análogo al puntero de C++ sin null , y en la medida en que el proceso de Simula I con una declaración ficticia como su cuerpo de actividad es análogo al struct de C++ (que en sí mismo es análogo al registro de CAR Hoare en el trabajo contemporáneo de la década de 1960), Simula I tenía elementos de conteo de referencias (es decir, expresiones de puntero que albergan indirección) a procesos (es decir, registros) a más tardar en septiembre de 1965, como se muestra en los párrafos citados a continuación. [5]

Los procesos pueden referenciarse individualmente. Físicamente, una referencia de proceso es un puntero a un área de memoria que contiene los datos locales del proceso y alguna información adicional que define su estado actual de ejecución. Sin embargo, por las razones indicadas en la Sección 2.2, las referencias de proceso son siempre indirectas, a través de elementos llamados elementos. Formalmente, una referencia a un proceso es el valor de una expresión de tipo elemento .
… los valores de
los elementos se pueden almacenar y recuperar mediante asignaciones y referencias a variables de elementos y por otros medios.
El lenguaje contiene un mecanismo para hacer que los atributos de un proceso sean accesibles desde el exterior, es decir, desde dentro de otros procesos. Esto se llama acceso remoto. Un proceso es, por tanto, una estructura de datos referenciable.

Vale la pena notar la similitud entre un proceso cuyo cuerpo de actividad es una declaración ficticia y el concepto de registro propuesto recientemente por CAR Hoare y N. Wirth.

Debido a que C++ tomó prestado el enfoque de Simula para la asignación de memoria (la palabra clave new al asignar un proceso/registro para obtener un elemento nuevo para ese proceso/registro), no es sorprendente que C++ eventualmente resucitara el mecanismo de puntero inteligente contado por referencia de Simula también dentro del elemento .

Características

En C++ , un puntero inteligente se implementa como una clase de plantilla que imita, mediante la sobrecarga de operadores , los comportamientos de un puntero tradicional (sin procesar) (por ejemplo, desreferenciación, asignación), al tiempo que proporciona funciones adicionales de gestión de memoria.

Los punteros inteligentes pueden facilitar la programación intencional al expresar, en el tipo, cómo se gestionará la memoria del referente del puntero. Por ejemplo, si una función de C++ devuelve un puntero, no hay forma de saber si el autor de la llamada debe eliminar la memoria del referente cuando haya terminado de utilizar la información.

SomeType * AmbiguousFunction (); // ¿Qué se debe hacer con el resultado?  

Tradicionalmente, se han utilizado convenciones de nombres para resolver la ambigüedad, [6] lo que es un enfoque propenso a errores y que requiere mucho trabajo. C++11 introdujo una forma de garantizar la gestión correcta de la memoria en este caso al declarar la función para que devuelva un unique_ptr,

std :: unique_ptr < AlgúnTipo > FunciónObvia (); 

La declaración del tipo de retorno de la función como a unique_ptrhace explícito el hecho de que quien la llama toma posesión del resultado y el entorno de ejecución de C++ garantiza que la memoria se recuperará automáticamente. Antes de C++11 , unique_ptr se podía reemplazar por auto_ptr , que ahora está en desuso.

Creando nuevos objetos

Para facilitar la asignación de una

std :: shared_ptr < AlgúnTipo >

Se introdujo C++11 :

auto s = std :: make_shared < SomeType > ( constructor , parámetros , aquí );     

y de manera similar

std :: unique_ptr < algún_tipo >

Desde C++14 se puede utilizar:

auto u = std :: make_unique < SomeType > ( constructor , parámetros , aquí );     

Es preferible, en casi todas las circunstancias, utilizar estas funciones en lugar de la newpalabra clave. [7]

PTR único

C++11 introduce std::unique_ptr, definido en el encabezado <memory>. [8]

A unique_ptres un contenedor para un puntero sin formato, del que unique_ptrse dice que es propietario. A unique_ptrimpide explícitamente la copia del puntero que contiene (como sucedería con una asignación normal), pero la std::movefunción se puede utilizar para transferir la propiedad del puntero que contiene a otro unique_ptr. A unique_ptrno se puede copiar porque su constructor de copia y los operadores de asignación se eliminan explícitamente.

std :: unique_ptr < int > p1 ( new int ( 5 )); std :: unique_ptr < int > p2 = p1 ; // Error de compilación. std :: unique_ptr < int > p3 = std :: move ( p1 ); // Transfiere la propiedad. p3 ahora posee la memoria y p1 está establecido en nullptr.          p3 . reset (); // Borra la memoria. p1 . reset (); // No hace nada.  

std::auto_ptrestá obsoleto en C++11 y se eliminó por completo de C++17 . El constructor de copia y los operadores de asignación de auto_ptren realidad no copian el puntero almacenado. En cambio, lo transfieren , dejando el objeto anterior auto_ptrvacío. Esta era una forma de implementar la propiedad estricta, de modo que solo un auto_ptrobjeto pueda poseer el puntero en un momento dado. Esto significa que auto_ptrno se debe usar donde se necesita semántica de copia. [9] [ cita requerida ] Dado que auto_ptrya existía con su semántica de copia, no se podía actualizar para que fuera un puntero de solo movimiento sin romper la compatibilidad con versiones anteriores del código existente.

shared_ptr y débil_ptr

C++11 introduce std::shared_ptry std::weak_ptr, definido en el encabezado <memory>. [8] C++11 también introduce std::make_shared( std::make_uniquese introdujo en C++14) para asignar memoria dinámica de forma segura en el paradigma RAII . [10]

A shared_ptres un contenedor para un puntero sin formato . Mantiene la propiedad de recuento de referencias de su puntero contenido en cooperación con todas las copias de . Un objeto al que hace referencia el puntero sin formato contenido se destruirá cuando y solo cuando se hayan destruido shared_ptrtodas las copias de .shared_ptr

std :: shared_ptr < int > p0 ( new int ( 5 )); // Válido, asigna 1 entero y lo inicializa con el valor 5. std :: shared_ptr < int [] > p1 ( new int [ 5 ]); // Válido, asigna 5 enteros. std :: shared_ptr < int [] > p2 = p1 ; // Ambos ahora poseen la memoria.          p1 . reset (); // La memoria todavía existe, debido a p2. p2 . reset (); // Libera la memoria, ya que nadie más es dueño de la memoria.  

A weak_ptres un contenedor para un puntero sin formato. Se crea como una copia de a shared_ptr. La existencia o destrucción de weak_ptrcopias de a shared_ptrno tiene efecto sobre el shared_ptro sus otras copias. Una vez que shared_ptrse han destruido todas las copias de a, todas weak_ptrlas copias quedan vacías.

std :: shared_ptr < int > p1 = std :: make_shared < int > ( 5 ); std :: debil_ptr < int > wp1 { p1 }; // p1 es el propietario de la memoria.      { std :: shared_ptr < int > p2 = wp1.lock (); // Ahora p1 y p2 son dueños de la memoria. // p2 se inicializa desde un puntero débil, por lo que debe verificar si la memoria aún existe. if ( p2 ) { DoSomethingWith ( p2 ); } } // p2 se destruye. La memoria es propiedad de p1.            p1 . reset (); // Libera la memoria. std :: shared_ptr < int > p3 = wp1.lock (); // La memoria se ha agotado, por lo que obtenemos un shared_ptr vacío.if ( p3 ) { // el código no se ejecutará ActionThatNeedsALivePointer ( p3 ); }        

Debido a que la implementación de shared_ptrusa el conteo de referencias , las referencias circulares son potencialmente un problema. Una shared_ptrcadena circular se puede romper modificando el código de modo que una de las referencias sea una weak_ptr.

Varios subprocesos pueden acceder de forma segura y simultánea a diferentes shared_ptrobjetos weak_ptrque apuntan al mismo objeto. [11]

El objeto referenciado debe protegerse por separado para garantizar la seguridad del hilo .

shared_ptry weak_ptrse basan en versiones utilizadas por las bibliotecas Boost . [ cita requerida ] El Informe técnico 1 de C++ (TR1) los introdujo por primera vez en el estándar, como utilidades generales , pero C++11 agrega más funciones, en línea con la versión Boost.

Otros tipos de punteros inteligentes

Hay otros tipos de punteros inteligentes (que no están en el estándar C++) implementados en bibliotecas populares de C++ o STL personalizados ; algunos ejemplos incluyen el puntero de peligro [12] y el puntero intrusivo. [13] [14]

Véase también

Referencias

  1. ^ Kline, Marshall (septiembre de 1997). "Secciones de C++ FAQ Lite sobre punteros inteligentes con recuento de referencias y semántica de referencias de copia en escritura en las preguntas frecuentes sobre administración de freestore". cis.usouthal.edu . Consultado el 6 de abril de 2018 .
  2. ^ Colvin, Gregory (1994). "Propuesta para estandarizar counted_ptr en la biblioteca estándar de C++" (PDF) . open-std.org . Consultado el 6 de abril de 2018 .
  3. ^ Klabnik, Steve; Nichols, Carol (2023) [2018]. "15. Punteros inteligentes". El lenguaje de programación Rust (2.ª edición). San Francisco, California, EE. UU.: No Starch Press, Inc., págs. 315–351. ISBN 978-1-7185-0310-6.(xxix+1+527+3 páginas)
  4. ^ Stroustrup, Bjarne. "Una historia de C++: 1979–1991" (PDF) . Consultado el 6 de abril de 2018 .
  5. ^ Dahl, Ole-Johan; Nygaard, Kristen (septiembre de 1966). "SIMULA: un lenguaje de simulación basado en ALGOL" (PDF) . folk.uio.no. ​Consultado el 6 de abril de 2018 .
  6. ^ "Guía de Taligent para el diseño de programas, sección Utilice nombres especiales para rutinas de copia, creación y adopción".
  7. ^ Sutter, Herb (20 de abril de 2013). "Informe de viaje: reunión de primavera de 2013 de ISO C++". isocpp.org . Consultado el 14 de junio de 2013 .
  8. ^ según ISO 14882:2011 20.7.1
  9. ^ CERT Estándar de codificación segura C++
  10. ^ ISO 14882:2014 20.7.1
  11. ^ "Seguridad del subproceso boost::shared_ptr".(NB. No cubre formalmente std::shared_ptr, pero se cree que tiene las mismas limitaciones de subprocesos).
  12. ^ "folly/Hazptr.h en principal · facebook/folly". github.com .
  13. ^ "Boost.SmartPtr: La biblioteca de punteros inteligentes - 1.81.0". boost.org .
  14. ^ "EASTL/intrusive_ptr.h en master · electronicarts/EASTL". github.com .

Lectura adicional

Enlaces externos