C ++ tiene que ver con la propiedad de la memoria, también conocida como semántica de propiedad .
Es responsabilidad del propietario de una parte de la memoria asignada dinámicamente liberar esa memoria. Entonces, la pregunta realmente es a quién pertenece la memoria.
En C ++, la propiedad está documentada por el tipo en el que se envuelve un puntero sin formato, por lo que en un buen programa (IMO) C ++ es muy raro ( raro , no nunca ) ver punteros sin procesar (ya que los punteros sin formato no tienen propiedad inferida, por lo que podemos no decir quién es el propietario 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 sin formato almacenados en una clase, cada puntero sin formato se almacena dentro de su propio contenedor de puntero inteligente. ( Nota: si no posee un objeto, no debería almacenarlo porque no puede saber cuándo saldrá del alcance y será destruido).
Entonces la pregunta:
- ¿Qué tipo de semántica de propiedad ha encontrado la gente?
- ¿Qué clases estándar se utilizan para implementar esa semántica?
- ¿En qué situaciones los encuentra útiles?
Mantengamos 1 tipo de propiedad semántica por respuesta para que se puedan votar hacia arriba y hacia abajo individualmente.
Resumen:
Conceptualmente, los punteros inteligentes son simples y una implementación ingenua es fácil. He visto muchos intentos de implementación, pero invariablemente se rompen de alguna manera que no es obvia para el uso casual y los ejemplos. Por lo tanto, recomiendo usar siempre punteros inteligentes bien probados de una biblioteca en lugar de usar los suyos propios. std::auto_ptr
o uno de los indicadores inteligentes de Boost parece cubrir todas mis necesidades.
std::auto_ptr<T>
:
Una sola persona es propietaria del objeto. Se permite la transferencia de propiedad.
Uso: esto le permite definir interfaces que muestran la transferencia explícita de propiedad.
boost::scoped_ptr<T>
Una sola persona es propietaria del objeto. NO se permite la transferencia de propiedad.
Uso: se utiliza para mostrar propiedad explícita. El objeto será destruido por el destructor 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 referencia llega a cero, el objeto se destruye.
Uso: cuando un objeto puede tener varias flores con una vida útil que no se puede determinar en el momento de la compilación.
boost::weak_ptr<T>
:
Se usa con shared_ptr<T>
en situaciones en las que puede ocurrir un ciclo de punteros.
Uso: Se utiliza para evitar que los ciclos retengan objetos cuando solo el ciclo mantiene un recuento de referencia compartido.
fuente
In C++ ownership is documented by the type a RAW pointer is wrapped inside thus in a good (IMO)
¿Se puede reformular esto? No lo entiendo en absoluto.In C++ ownership is documented by the type a RAW pointer is wrapped inside thus in a good C++ program it is very rare to see RAW pointers passed around
. Los punteros RAW no tienen semántica de propiedad. Si no conoce al propietario, no sabe quién es responsable de eliminar el objeto. Hay varias clases estándar que se utilizan para ajustar punteros (std :: shared_ptr, std :: unique_ptr, etc.) que definen la propiedad y, por lo tanto, defina quién es responsable de eliminar el puntero.Respuestas:
Para mí, estos 3 tipos cubren la mayoría de mis necesidades:
shared_ptr
- referencia contada, desasignación cuando el contador llega a ceroweak_ptr
- igual que el anterior, pero es un 'esclavo' para unshared_ptr
, no se puede desasignarauto_ptr
- cuando la creación y la desasignación ocurren dentro de la misma función, o cuando el objeto debe considerarse de un solo propietario. 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 formato
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á vivo solo durante el tiempo de la llamada.Hay otro tipo de puntero que uso al que llamo hub_ptr . Es cuando tiene un objeto que debe ser accesible desde objetos anidados en él (generalmente como una clase base virtual). Esto podría resolverse pasándoles un
weak_ptr
a, pero no tiene unshared_ptr
a sí mismo. Como sabe que estos objetos no vivirían más que él, les pasa un hub_ptr (es solo un contenedor de plantilla para un puntero normal).fuente
noncopyable
y que la propiedad no se puede transferir.Modelo C ++ simple
En la mayoría de los módulos que vi, de forma predeterminada, se asumió que recibir punteros no estaba recibiendo propiedad. De hecho, las funciones / métodos que abandonaron la propiedad de un puntero fueron muy raros y expresaron explícitamente ese hecho en su documentación.
Este modelo asume que el usuario es propietario solo de lo que asigna explícitamente . Todo lo demás se elimina automáticamente (en la salida del osciloscopio o mediante RAII). Este es un modelo similar a C, ampliado 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 mayoría de los casos, en la destrucción de dichos objetos), y que la duración de vida de los objetos es predecible (RAII es su amigo, de nuevo).
En este modelo, los punteros sin procesar circulan libremente y en su mayoría no son peligrosos (pero si el desarrollador es lo suficientemente inteligente, usará referencias siempre que sea posible).
Modelo C ++ con punta inteligente
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í mismo (RAII, nuevamente). El problema es que las referencias circulares mezcladas con punteros inteligentes contados por referencia pueden ser mortales , por lo que debe lidiar tanto con punteros compartidos como con punteros débiles. Por lo tanto, todavía tiene que considerar la propiedad (el puntero débil bien podría apuntar a nada, incluso si su ventaja sobre el puntero sin formato es que puede decírselo).
Conclusión
Independientemente de los modelos que describa, a menos que sea una excepción, recibir un puntero no es recibir su propiedad y sigue siendo muy importante saber quién posee a quién . Incluso para el código C ++ que usa mucho referencias y / o punteros inteligentes.
fuente
No tengo propiedad compartida. Si lo hace, asegúrese de que sea solo con el código que no controla.
Eso resuelve el 100% de los problemas, ya que te obliga a entender cómo interactúa todo.
fuente
Cuando un recurso se comparte entre varios objetos. El boost shared_ptr usa el recuento de referencias para asegurarse de que el recurso sea desasignado cuando todos hayan terminado.
fuente
std::tr1::shared_ptr<Blah>
es a menudo su mejor apuesta.fuente
Desde boost, también está la biblioteca de contenedores de punteros . 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, existen los punteros COM (IUnknown, IDispatch y amigos) y varios punteros inteligentes para manejarlos (por ejemplo, CComPtr de ATL y los punteros inteligentes generados automáticamente por la instrucción "import" en Visual Studio basada en la clase _com_ptr ).
fuente
Cuando necesita asignar memoria de forma dinámica pero quiere asegurarse de que se desasigne en cada punto de salida del bloque.
Encuentro esto útil, ya que se puede volver a colocar y liberar fácilmente sin tener que preocuparme por una fuga.
fuente
Creo que nunca estuve en condiciones de compartir la propiedad de mi diseño. De hecho, desde lo alto de mi cabeza, el único caso válido en el que puedo pensar es el patrón Flyweight.
fuente
yasper :: ptr es una alternativa ligera de boost :: shared_ptr. 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:
fuente
Existe otra forma de propietario único transferible que se usa con frecuencia, y es preferible
auto_ptr
porque evita los problemas causados porauto_ptr
la loca corrupción de la semántica de asignación.No hablo de otro que
swap
. Cualquier tipo con unaswap
función adecuada puede concebirse como una referencia inteligente a algún contenido, que es de su propiedad hasta el momento en que la propiedad se transfiere a otra instancia del mismo tipo, intercambiándolos. Cada instancia conserva su identidad pero está vinculada a contenido nuevo. Es como una referencia que se puede volver a enlazar con seguridad.(Es una referencia inteligente en lugar de un puntero inteligente porque no es necesario eliminarla explícitamente para acceder al contenido).
Esto significa que auto_ptr se vuelve menos necesario; solo se necesita para llenar los espacios donde los tipos no tienen una buena
swap
función. Pero todos los contenedores estándar lo hacen.fuente
Cuando el creador del objeto quiere ceder explícitamente la propiedad a otra persona. Esta es también una forma de documentar en el código que le estoy dando y ya no lo estoy rastreando, así que asegúrese de eliminarlo cuando haya terminado.
fuente