En informática , un puntero inteligente es un tipo de datos abstracto que simula un puntero al tiempo que proporciona funciones adicionales, como la gestión automática de la memoria o la comprobación de límites . Dichas características están destinadas a reducir los errores causados por el mal uso de los punteros, al tiempo que mantienen la eficiencia. Los punteros inteligentes suelen realizar un seguimiento de la memoria a la que apuntan y también se pueden utilizar para administrar otros recursos, como las conexiones de red y los identificadores 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 a la falta de recolección automática de basura de C ++ . [1][2]
El mal uso del puntero puede ser una fuente importante de errores. Los punteros inteligentes evitan la mayoría de las situaciones de pérdida de memoria al hacer que la desasignación de memoria sea automática. De manera más general, hacen que la destrucción de objetos sea automática: un objeto controlado por un puntero inteligente se destruye automáticamente ( 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 alcance de la variable . Los punteros inteligentes también eliminan los punteros colgantes posponiendo 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 la memoria, pero son útiles para otros propósitos, como la administración de la residencia de la estructura de datos de la caché y la administración de recursos de los objetos. como identificadores de archivos o sockets de red .
Existen varios tipos de punteros inteligentes. Algunos funcionan con el recuento de referencias , otros al asignar la propiedad de un objeto a un puntero.
Historia
Aunque C ++ popularizó el concepto de punteros inteligentes, especialmente la variedad contada por referencias , el predecesor inmediato de uno de los lenguajes que inspiraron el diseño de C ++ tenía referencias contadas por referencias integradas en el lenguaje. C ++ se inspiró en parte en Simula67. [3] El antepasado de Simula67 fue Simula I. En la medida en que el elemento de Simula I es análogo al puntero de C ++ sin nulo , y en la medida en que el proceso de Simula I con una declaración ficticia como cuerpo de actividad es análogo a la estructura de C ++ (que a su vez es análoga a la de CAR Hoare registro en el trabajo contemporáneo de 1960), Simula I hizo referencia a elementos contados (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. [4]
Los procesos se pueden referenciar 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 expuestas en la Sección 2.2, las referencias a procesos son siempre indirectas, a través de elementos llamados elementos. Formalmente, una referencia a un proceso es el valor de una expresión de elemento de tipo .
… 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 accesibles los atributos de un proceso desde el exterior, es decir, desde dentro de otros procesos. A esto se le llama acceso remoto. Por tanto, un proceso es 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 nueva palabra clave cuando se asigna un proceso / registro para obtener un elemento nuevo para ese proceso / registro, no es sorprendente que C ++ finalmente resucitara el mecanismo de puntero inteligente contado por referencias de Simula dentro del elemento como bien.
Características
En C ++ , un puntero inteligente se implementa como una clase de plantilla que imita, mediante la sobrecarga del operador , los comportamientos de un puntero tradicional (sin procesar) (por ejemplo, desreferenciación, asignación) al tiempo que proporciona funciones adicionales de administració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 la persona que llama debe borrar la memoria del referente cuando la persona que llama ha terminado con la información.
SomeType * AmbiguousFunction (); // ¿Qué se debe hacer con el resultado?
Tradicionalmente, las convenciones de nomenclatura se han utilizado para resolver la ambigüedad, [5] que es un enfoque laborioso y propenso a errores. C ++ 11 introdujo una forma de garantizar la gestión correcta de la memoria en este caso al declarar que la función devuelve un unique_ptr
,
std :: unique_ptr < SomeType > ObviousFunction ();
La declaración del tipo de retorno de la función como a unique_ptr
hace explícito el hecho de que la persona que llama se apropia del resultado, y el tiempo de ejecución de C ++ asegura que la memoria se reclamará automáticamente. Antes de C ++ 11 , unique_ptr se puede reemplazar con auto_ptr .
Creando nuevos objetos
Para facilitar la asignación de un
std :: shared_ptr < SomeType >
C ++ 11 introducido:
automóviles s = std :: make_shared < SomeType > ( constructor , parámetros , aquí );
y de manera similar
std :: unique_ptr < some_type >
Desde C ++ 14 uno puede usar:
automóvil u = std :: make_unique < SomeType > ( constructor , parámetros , aquí );
Se prefiere, en casi todas las circunstancias, utilizar estas funciones en lugar de la new
palabra clave: [6]
unique_ptr
C ++ 11 introduce std::unique_ptr
, definido en el encabezado
. [7]
A unique_ptr
es un contenedor para un puntero sin formato, que unique_ptr
se dice que posee. A unique_ptr
evita explícitamente la copia de su puntero contenido (como sucedería con una asignación normal), pero la std::move
función se puede usar para transferir la propiedad del puntero contenido a otro unique_ptr
. A unique_ptr
no se puede copiar porque su constructor de copia y los operadores de asignación se eliminan explícitamente.
std :: unique_ptr < int > p1 ( nuevo int ( 5 )); std :: unique_ptr < int > p2 = p1 ; // Error de compilación. std :: unique_ptr < int > p3 = std :: mover ( p1 ); // Transfiere la propiedad. p3 ahora es dueño de la memoria y p1 se establece en nullptr.p3 . restablecer (); // Borra la memoria. p1 . restablecer (); // No hace nada.
std::auto_ptr
está obsoleto en C ++ 11 y completamente eliminado de C ++ 17 . El constructor de copia y los operadores de asignación de auto_ptr
no copian realmente el puntero almacenado. En cambio, lo transfieren , dejando auto_ptr
vacío el objeto anterior . Esta fue una forma de implementar la propiedad estricta, de modo que solo un auto_ptr
objeto pueda poseer el puntero en un momento dado. Esto significa que auto_ptr
no debe usarse donde se necesite una semántica de copia. [8] [ cita requerida ] Dado que auto_ptr
ya existía con su semántica de copia, no se podía actualizar para ser un puntero de solo movimiento sin romper la compatibilidad con el código existente.
C ++ 11 introduce std::shared_ptr
y std::weak_ptr
, definido en el encabezado
. [7] C ++ 11 también introduce std::make_shared
( std::make_unique
se introdujo en C ++ 14) para asignar de forma segura memoria dinámica en el paradigma RAII . [9]
A shared_ptr
es 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 del shared_ptr
. Un objeto al que hace referencia el puntero sin formato contenido se destruirá cuando y solo cuando shared_ptr
se hayan destruido todas las copias del .
std :: shared_ptr < int > p0 ( nuevo 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 . restablecer (); // La memoria todavía existe, debido a p2. p2 . restablecer (); // Libera la memoria, ya que nadie más posee la memoria.
A weak_ptr
es un contenedor para un puntero sin formato. Se crea como una copia de un shared_ptr
. La existencia o destrucción de weak_ptr
copias de un shared_ptr
no tiene ningún efecto sobre el shared_ptr
o sus otras copias. Una shared_ptr
vez destruidas todas las weak_ptr
copias de a , todas las copias quedan vacías.
std :: shared_ptr < int > p1 = std :: make_shared < int > ( 5 ); std :: débil_ptr < int > wp1 { p1 }; // p1 posee la memoria.{ std :: shared_ptr < int > p2 = wp1 . bloquear (); // Ahora p1 y p2 poseen la memoria. // ¡p2 se inicializa desde un puntero débil, por lo que debe verificar si la // memoria todavía existe! if ( p2 ) { Hacer algo con ( p2 ); } } // p2 se destruye. La memoria es propiedad de p1.p1 . restablecer (); // Libera la memoria.std :: shared_ptr < int > p3 = wp1 . bloquear (); // La memoria se ha ido, por lo que obtenemos un shared_ptr vacío. if ( p3 ) { // el código no ejecutará ActionThatNeedsALivePointer ( p3 ); }
Debido a que la implementación del recuento de referencias de shared_ptr
usos , las referencias circulares son potencialmente un problema. Una cadena circular se puede romper cambiando el código para que una de las referencias sea un .shared_ptr
weak_ptr
Varios subprocesos pueden acceder de forma segura y simultánea a diferentes objetos shared_ptr
y weak_ptr
que apuntan al mismo objeto. [10]
El objeto referenciado debe protegerse por separado para garantizar la seguridad de los hilos .
shared_ptr
y weak_ptr
se basan en versiones utilizadas por las bibliotecas de Boost . [ cita requerida ] C ++ Technical Report 1 (TR1) los introdujo por primera vez al estándar, como utilidades generales , pero C ++ 11 agrega más funciones, en línea con la versión Boost.
Ver también
- Recuento automático de referencias
- La adquisición de recursos es inicialización (RAII)
- auto_ptr
- Puntero opaco
- Referencia (informática)
- Boost (bibliotecas de C ++)
- Puntero gordo
- Recolección de basura en programación informática
Referencias
- ^ Kline, Marshall (septiembre de 1997). "Las secciones de C ++ FAQs Lite sobre punteros inteligentes contados por referencias y semántica de referencia de copia en escritura en las Preguntas frecuentes sobre la gestión de la tienda gratuita" . cis.usouthal.edu . Consultado el 6 de abril de 2018 .
- ^ Colvin, Gregory (1994). "propuesta para estandarizar countted_ptr en la biblioteca estándar de C ++" (PDF) . open-std.org . Consultado el 6 de abril de 2018 .
- ^ Stroustrup, Bjarne. "Una historia de C ++: 1979-1991" (PDF) . Consultado el 6 de abril de 2018 .
- ^ Dahl, Ole-Johan y 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 .CS1 maint: varios nombres: lista de autores ( enlace )
- ^ "Guía de Taligent para diseñar programas, sección Usar nombres especiales para copiar, crear y adoptar rutinas" .
- ^ Sutter, Herb (20 de abril de 2013). "Informe de viaje: Reunión ISO C ++ Primavera 2013" . isocpp.org . Consultado el 14 de junio de 2013 .
- ^ a b ISO 14882: 2011 20.7.1
- ^ Estándar de codificación segura CERT C ++
- ^ ISO 14882: 2014 20.7.1
- ^ boost :: shared_ptr seguridad de subprocesos (no cubre formalmente std :: shared_ptr, pero se cree que tiene las mismas limitaciones de subprocesos)
Otras lecturas
- Scott Meyers (2014). C ++ moderno eficaz . Sebastopol, CA: O'Reilly Media . ISBN 978-1491903995. OCLC 884480640 .
enlaces externos
- Punteros inteligentes . Diseño moderno en C ++ : programación genérica y patrones de diseño aplicados por Andrei Alexandrescu , Addison-Wesley, 2001.
- countptr.hpp . La biblioteca estándar de C ++: un tutorial y una referencia de Nicolai M. Josuttis
- Impulse los punteros inteligentes
- El nuevo C ++: punteros inteligentes (más) . Herb Sutter 1 de agosto de 2002
- Punteros inteligentes: ¿qué, por qué, cuáles? . Yonat Sharon
- Descripción general de Smart Pointers . John M. Dlugosz
- Punteros inteligentes en Delphi
- Punteros inteligentes en óxido
- Punteros inteligentes en C ++ moderno