Pregunta

C++ tiene que ver con la propiedad de la memoria
También conocido como "Semántica de propiedad"

Es responsabilidad del propietario de un fragmento de memoria asignada dinámicamente liberar esa memoria.Entonces la pregunta realmente es quién es el dueño de la memoria.

En C++, la propiedad está documentada por el tipo en el que se incluye un puntero RAW, por lo que en un buen programa (IMO) de C++ es muy raro [RARO, NUNCA] ver punteros RAW pasados ​​(ya que los punteros RAW no tienen propiedad inferida, por lo que no podemos decir quién es el propietario de la memoria y, por lo tanto, sin una lectura cuidadosa de la documentación no se puede saber quién es el responsable de la propiedad).

Por el contrario, es raro ver punteros RAW almacenados en una clase en la que cada puntero RAW se almacena dentro de su propio contenedor de puntero SMART.(NÓTESE BIEN.: Si no posee un objeto, no debería almacenarlo porque no puede saber cuándo quedará fuera de alcance y será destruido).

Entonces la pregunta:

  • ¿Con qué tipo de propiedad semántica se ha topado la gente?
  • ¿Qué clases estándar se utilizan para implementar esa semántica?
  • ¿En qué situaciones los encuentras útiles?

Mantengamos 1 tipo de propiedad semántica por respuesta para que puedan votarse a favor o en contra individualmente

Resumen:

Los punteros conceptualmente inteligentes son simples y las implementaciones ingenuas son fáciles.He visto muchos intentos de implementación, pero invariablemente están rotos de alguna manera que no es obvia para el uso casual y los ejemplos.Por lo tanto, recomiendo utilizar siempre "punteros inteligentes" bien probados de una biblioteca en lugar de utilizar los suyos propios.std::auto_ptr o uno de los punteros inteligentes de impulso parecen cubrir todas mis necesidades.

std::auto_ptr<T>:

Una sola persona es propietaria del objeto.
Pero se permite la transferencia de propiedad.

Uso:
======
Esto le permite definir interfaces que muestran la transferencia explícita de propiedad.

impulso::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.

impulso::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 el objeto puede tener múltiples poderes con una vida útil que no se puede determinar en el momento de la compilación.

impulso::weak_ptr<T>

Usado con share_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 compartido.

¿Fue útil?

Solución

Para mí, estos 3 tipos cubren la mayoría de mis necesidades:

shared_ptr - conteo de referencia, desasignación cuando el contador llega a cero

weak_ptr - Igual que el anterior, pero es un 'esclavo' para un shared_ptr, no se puede desasignar

auto_ptr - cuando la creación y desasignación ocurren dentro de la misma función, o cuando el objeto debe considerarse de un solo propietario.Cuando asignas 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á vivo sólo durante el tiempo de la llamada.

Hay otro tipo de puntero que uso al que llamo hub_ptr.Es cuando tienes un objeto al que se debe poder acceder desde objetos anidados en él (normalmente como una clase base virtual).Esto se podría solucionar pasando un weak_ptr para ellos, pero no tiene un shared_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).

Otros consejos

Modelo C++ sencillo

En la mayoría de los módulos que vi, de forma predeterminada, se suponía que recibir punteros era no recibiendo la propiedad.De hecho, las funciones/métodos que abandonaban la propiedad de un puntero eran muy raros y expresaban explícitamente ese hecho en su documentación.

Este modelo supone que el usuario es propietario únicamente de lo que asigna explícitamente..Todo lo demás se elimina automáticamente (al salir del alcance o mediante RAII).Este es un modelo tipo C, ampliado por el hecho de que la mayoría de los punteros pertenecen a objetos que los desasignarán automáticamente o cuando sea necesario (en la destrucción de dichos objetos, en su mayoría), y que la duración de la 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, utilizará referencias siempre que sea posible).

  • punteros crudos
  • std::auto_ptr
  • impulso::scoped_ptr

Modelo C++ puntiagudo 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 propio puntero inteligente (RAII, de nuevo). El problema es que las referencias circulares mezcladas con punteros inteligentes contados por referencias pueden ser mortales., por lo que debe lidiar tanto con los punteros compartidos como con los débiles.Por lo tanto, aún debe considerar la propiedad (el puntero débil bien podría indicar nada, incluso si su ventaja sobre el puntero sin formato es que puede decírselo).

  • impulso::shared_ptr
  • impulso::weak_ptr

Conclusión

No importa los modelos que describo, A menos que haya una excepción, recibir un puntero es no recibiendo su propiedad y Sigue siendo muy importante saber quién es el dueño de quién..Incluso para código C++ que utiliza mucho referencias y/o punteros inteligentes.

No tener propiedad compartida.Si lo haces, asegúrate de que sea sólo con código que no controlas.

Eso soluciona el 100% de los problemas, ya que te obliga a entender cómo interactúa todo.

  • Propiedad compartida
  • impulso::shared_ptr

Cuando un recurso se comparte entre varios objetos.El impulso compartido_ptr utiliza el recuento de referencias para garantizar que el recurso se desasigne cuando todos hayan terminado.

std::tr1::shared_ptr<Blah> suele ser la mejor opción.

De impulso, también está el contenedor de puntero biblioteca.Estos son un poco más eficientes y fáciles de usar que un contenedor estándar de punteros inteligentes, si solo usa los objetos en el contexto de su contenedor.

En Windows, existen punteros COM (IUnknown, IDispatch y amigos) y varios punteros inteligentes para manejarlos (p. ej.los ATL CComPtr y los punteros inteligentes generados automáticamente por la declaración "importar" en Visual Studio según el _com_ptr clase).

  • Un dueño
  • impulso::scoped_ptr

Cuando necesita asignar memoria dinámicamente pero quiere asegurarse de que se desasigne en cada punto de salida del bloque.

Esto me parece útil ya que se puede volver a colocar y soltar fácilmente sin tener que preocuparse por una fuga.

No creo que alguna vez estuve en condiciones de compartir la propiedad de mi diseño.De hecho, desde lo más alto de mi cabeza, el único caso válido que se me ocurre es el patrón Flyweight.

yasper::ptr es una alternativa ligera tipo 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:

¿Por qué escribir otro puntero inteligente de C++?Ya existen varias implementaciones de puntero Smart de alta calidad para C ++, más prominentemente el Pantheon de Pointer Boost y SmartPTR de Loki.Para una buena comparación de las implementaciones de puntero inteligente y cuando su uso sea apropiado, lea el nuevo C ++ de Herb Sutter:Punteros (más) inteligentes.En contraste con las características expansivas de otras bibliotecas, Yasper es un puntero de conteo de referencia estrechamente enfocado.Corresponde estrechamente con las políticas de refuntado/permitido de BOOST y las políticas de Loki REFOUNTADO/PERMISCONVERSION.Yasper permite que los programadores de C ++ se olviden de la gestión de la memoria sin introducir las grandes dependencias del impulso o tener que aprender sobre 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 riesgosas (pero útiles) (como la asignación a punteros sin procesar y la liberación manual) no permitidas por otras implementaciones.¡Tenga cuidado, solo use esas características si sabe lo que está haciendo!

Existe otra forma de propietario único transferible que se utiliza con frecuencia y es preferible auto_ptr porque evita los problemas causados ​​por auto_ptrLa loca corrupción de la semántica de asignaciones.

hablo de nada menos que swap.Cualquier tipo con un adecuado swap La función puede ser concebida como una referencia inteligente a unos contenidos, de los que es propietario hasta que se transfiera la propiedad a otra instancia del mismo tipo, mediante el intercambio de los mismos.Cada instancia conserva su identidad pero se vincula a contenido nuevo.Es como una referencia que se puede volver a vincular de forma segura.

(Es una referencia inteligente en lugar de un puntero inteligente porque no es necesario eliminar la referencia explícitamente para acceder al contenido).

Esto significa que auto_ptr se vuelve menos necesario: sólo es necesario para llenar los vacíos donde los tipos no tienen una buena swap función.Pero todos los contenedores estándar lo hacen.

  • Un dueño:También conocido como eliminar al copiar
  • std::auto_ptr

Cuando el creador del objeto quiere entregar explícitamente la propiedad a otra persona.Esta también es una forma de documentar el código que le estoy dando y ya no lo estoy rastreando, así que asegúrese de eliminarlo cuando haya terminado.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top