c++ - Punteros inteligentes: ¿O quién te posee bebé?
memory-management smart-pointers (11)
Modelo simple de C ++
En la mayoría de los módulos que vi, de forma predeterminada, se suponía que recibir punteros no recibía la propiedad. De hecho, las funciones / métodos que abandonan la propiedad de un puntero fueron muy raros y expresaron explícitamente ese hecho en su documentación.
Este modelo supone que el usuario es el único propietario de lo que asigna explícitamente . Todo lo demás se elimina automáticamente (en la salida del alcance, o a través de RAII). Este es un modelo tipo C, extendido por el hecho de que la mayoría de los punteros son propiedad de objetos que los desasignarán automáticamente o cuando sea necesario (en la destrucción de dichos objetos, principalmente), y que la duración de vida de los objetos sea predecible (RAII es su amigo, de nuevo).
En este modelo, los punteros crudos circulan libremente y en su mayoría no son peligrosos (pero si el desarrollador es lo suficientemente inteligente, usará referencias siempre que sea posible).
- punteros crudos
- std :: auto_ptr
- boost :: scoped_ptr
Modelo Smart Point C ++
En un código lleno de punteros inteligentes, el usuario puede esperar ignorar la vida útil de los objetos. El propietario nunca es el código de usuario: es el puntero inteligente en sí (RAII, nuevamente). El problema es que las referencias circulares mezcladas con los punteros inteligentes contados de referencia pueden ser mortales , por lo que debe tratar tanto con los punteros compartidos como con los punteros débiles. Por lo tanto, aún debe tener en cuenta la propiedad (el puntero débil podría no señalar nada, incluso si su ventaja sobre el puntero sin procesar es que puede indicarlo).
- boost :: shared_ptr
- boost :: weak_ptr
Conclusión
No importa los modelos que describo, a menos que sea una excepción, recibir un puntero no es lo que le pertenece y aún es muy importante saber a quién pertenece . Incluso para el código C ++ se usan mucho las referencias y / o los punteros inteligentes.
C ++ se trata de la propiedad de la memoria
Aka " Semántica de propiedad "
Es responsabilidad del propietario de una porción de memoria asignada dinámicamente liberar esa memoria. Entonces la pregunta realmente se convierte en quién posee la memoria.
En C ++ la propiedad está documentada por el tipo de un puntero RAW dentro de un buen programa (IMO) C ++. Es muy raro [RARO NO NUNCA] ver punteros RAW pasados (ya que los punteros RAW no tienen una propiedad inferida por lo tanto no podemos diga quién es el dueño de la memoria y, por lo tanto, sin una lectura cuidadosa de la documentación, no puede decir quién es el responsable de la propiedad).
Por el contrario, es raro ver punteros RAW almacenados en una clase, cada puntero RAW se almacena dentro de su propio contenedor SMART Pointer. ( NB: si no posee un objeto, no debería almacenarlo porque no puede saber cuándo quedará fuera del alcance y se destruirá).
Entonces la pregunta:
- ¿Qué tipo de Semántica de Propiedad tiene la gente se encuentra?
- ¿Qué clases estándar se usan para implementar esa semántica?
- ¿En qué situaciones los encuentras útiles?
Permite mantener 1 tipo de propiedad semántica por respuesta para que puedan votarse de manera ascendente y descendente individualmente
Resumen:
Los indicadores conceptualmente inteligentes son simples y las implementaciones ingenuas son fáciles. He visto muchas implementaciones intentadas, pero invariablemente están rotas de alguna manera que no es obvia para uso casual y ejemplos. Por lo tanto, recomiendo usar siempre "Smart Pointers" bien probados en una biblioteca en lugar de hacer los suyos. std :: auto_ptr o uno de los indicadores inteligentes de boost parecen cubrir todas mis necesidades.
std :: auto_ptr <T>:
Una sola persona posee el objeto.
Pero la transferencia de propiedad está permitida.
Uso:
======
Esto le permite definir interfaces que muestran la transferencia explícita de propiedad.
boost :: scoped_ptr <T>
Una sola persona posee el objeto.
La transferencia de propiedad NO está permitida.
Uso:
======
Usado para mostrar propiedad explícita.
El destructor destruirá el objeto o cuando se restablezca explícitamente.
boost :: shared_ptr <T> (std :: tr1 :: shared_ptr <T>)
Propiedad múltiple
Este es un puntero contado de referencia simple. Cuando el recuento de referencias llega a cero, el objeto se destruye.
Uso:
======
Cuando object puede tener múltiples owers con una duración que no se puede determinar en tiempo de compilación.
boost :: weak_ptr <T>
Usado con shared_ptr <T>.
En situaciones donde puede suceder un ciclo de indicadores.
Uso:
======
Se usa para detener ciclos de retención de objetos cuando solo el ciclo mantiene un recuento compartido.
Desde el impulso, también está la biblioteca de contenedor de puntero . Estos son un poco más eficientes y fáciles de usar que un contenedor estándar de punteros inteligentes, si solo usará los objetos en el contexto de su contenedor.
En Windows, están los punteros COM (IUnknown, IDispatch y amigos) y varios punteros inteligentes para manejarlos (por ejemplo, el CComPtr de ATL y los punteros inteligentes autogenerados por la instrucción "import" en Visual Studio basada en la clase _com_ptr )
Hay otra forma de propietario único transferible que se usa con frecuencia, y es preferible que auto_ptr
porque evita los problemas causados por la corrupción insana de la semántica de la asignación de auto_ptr
.
Hablo de nada menos que swap
. Cualquier tipo con una función de swap
adecuada puede concebirse como una referencia inteligente de algún contenido, que posee hasta que se transfiere la propiedad a otra instancia del mismo tipo, intercambiándolos. Cada instancia conserva su identidad, pero se vincula al nuevo contenido. Es como una referencia reenganchable segura.
(Es una referencia inteligente en lugar de un puntero inteligente porque no tiene que desreferencia explícitamente para obtener el contenido).
Esto significa que auto_ptr se vuelve menos necesario; solo es necesario para llenar los espacios donde los tipos no tienen una buena función de swap
. Pero todos los contenedores estándar lo hacen.
No creo que alguna vez haya estado en posición de compartir la propiedad de mi diseño. De hecho, desde el comienzo de mi cabeza, el único caso válido en el que puedo pensar es el patrón Flyweight.
No tiene propiedad compartida. Si lo haces, asegúrate de que sea solo con código que no controlas.
Eso resuelve el 100% de los problemas, ya que te obliga a entender cómo interactúa todo.
Para mí, estos 3 tipos cubren la mayoría de mis necesidades:
shared_ptr
- shared_ptr
referencias, desasignación cuando el contador llega a cero
weak_ptr
: igual que el anterior, pero es un "esclavo" para un shared_ptr
, no puede desasignar
auto_ptr
: cuando la creación y la desasignación suceden dentro de la misma función, o cuando el objeto debe considerarse de un solo propietario para siempre. Cuando asigna un puntero a otro, el segundo ''roba'' el objeto del primero.
Tengo mi propia implementación para estos, pero también están disponibles en Boost
.
Todavía paso objetos por referencia ( const
siempre que sea posible), en este caso el método llamado debe asumir que el objeto está activo solo durante el tiempo de llamada.
Hay otro tipo de puntero que uso que llamo hub_ptr . Es cuando tienes un objeto que debe ser accesible desde objetos anidados en él (generalmente como una clase base virtual). Esto se puede resolver pasando un weak_ptr
a ellos, pero no tiene un shared_ptr
a sí mismo. Como sabe que estos objetos no vivirían más tiempo que él, les pasa un hub_ptr (es solo un contenedor de plantilla a un puntero regular).
yasper :: ptr es ligero, boost :: shared_ptr como alternativa. Funciona bien en mi (por ahora) pequeño proyecto.
En la página web en http://yasper.sourceforge.net/ se describe de la siguiente manera:
¿Por qué escribir otro puntero inteligente en C ++? Ya existen varias implementaciones de puntero inteligente de alta calidad para C ++, el más destacado es el panteón puntero Boost y el SmartPtr de Loki. Para una buena comparación de las implementaciones de punteros inteligentes y cuando su uso sea apropiado, lea The New C ++: Smart (er) Punteros de Herb Sutter. En contraste con las características expansivas de otras bibliotecas, Yasper es un puntero de conteo de referencia estrechamente enfocado. Se corresponde estrechamente con las políticas shared_ptr de Boost y RefCounted / AllowConversion de Loki. Yasper permite a los programadores de C ++ olvidarse de la administración de la memoria sin introducir las grandes dependencias de Boost o tener que aprender acerca de las complicadas plantillas de políticas de Loki. Filosofía
* small (contained in single header) * simple (nothing fancy in the code, easy to understand) * maximum compatibility (drop in replacement for dumb pointers)
El último punto puede ser peligroso, ya que yasper permite acciones arriesgadas (aunque útiles) (como la asignación a punteros crudos y liberación manual) no autorizadas por otras implementaciones. Tenga cuidado, solo use esas características si sabe lo que está haciendo.
std::tr1::shared_ptr<Blah>
es a menudo tu mejor apuesta.
- Propiedad compartida
- boost :: shared_ptr
Cuando un recurso se comparte entre múltiples objetos. El boost shared_ptr utiliza el recuento de referencias para asegurarse de que el recurso se desasigne cuando todos terminen.
- Un dueño
- boost :: scoped_ptr
Cuando necesita asignar memoria dinámicamente, pero quiere asegurarse de que se desasigna en cada punto de salida del bloque.
Encuentro esto útil, ya que puede volverse a colocar y liberar fácilmente sin tener que preocuparme por una fuga
- Un propietario: Eliminar Aka en Copia
- std :: auto_ptr
Cuando el creador del objeto quiere darle propiedad explícitamente a otra persona. Esta es también una forma de documentar en el código que te estoy dando y ya no estoy rastreando así que asegúrate de eliminarla cuando hayas terminado.