Ok, entonces la última vez que escribí C ++ para ganarme la vida, std::auto_ptr
era todo lo que tenía disponible la biblioteca estándar , y boost::shared_ptr
estaba de moda. Realmente nunca examiné el aumento de otros tipos de punteros inteligentes proporcionados. Entiendo que C ++ 11 ahora proporciona algunos de los tipos que se le ocurrieron, pero no todos.
Entonces, ¿alguien tiene un algoritmo simple para determinar cuándo usar qué puntero inteligente? Preferiblemente incluye consejos sobre punteros tontos (como punteros crudos T*
) y el resto de los punteros inteligentes de impulso. (Algo como esto sería genial).
Respuestas:
Propiedad compartida:
El
shared_ptr
yweak_ptr
el estándar adoptado son más o menos lo mismo que sus contrapartes Boost . Úselos cuando necesite compartir un recurso y no sepa cuál será el último en estar vivo. Se usaweak_ptr
para observar el recurso compartido sin influir en su vida útil, no para romper ciclos. Los ciclos conshared_ptr
normalmente no deberían suceder: dos recursos no pueden ser dueños el uno del otro.Tenga en cuenta que Boost también ofrece
shared_array
, que podría ser una alternativa adecuada ashared_ptr<std::vector<T> const>
.A continuación, Boost ofrece
intrusive_ptr
, que son una solución ligera si su recurso ya ofrece una gestión contada por referencia y desea adoptarla según el principio RAII. Este no fue adoptado por la norma.Propiedad única:
Boost también tiene un
scoped_ptr
, que no es copiable y para el que no puede especificar un borrador.std::unique_ptr
estáboost::scoped_ptr
en esteroides y debería ser su opción predeterminada cuando necesita un puntero inteligente . Le permite especificar un eliminador en sus argumentos de plantilla y es móvil , a diferenciaboost::scoped_ptr
. También es totalmente utilizable en contenedores STL siempre que no utilice operaciones que necesiten tipos copiables (obviamente).Tenga en cuenta nuevamente que Boost tiene una versión de matriz:
scoped_array
que el estándar se unificó al requerirstd::unique_ptr<T[]>
una especialización parcial que utilizarádelete[]
el puntero en lugar dedelete
utilizarlo (con ladefault_delete
r).std::unique_ptr<T[]>
también ofrece enoperator[]
lugar deoperator*
yoperator->
.Tenga en cuenta que
std::auto_ptr
todavía está en el estándar, pero está en desuso .§D.10 [depr.auto.ptr]
Sin propiedad:
utilice punteros tontos (punteros sin procesar) o referencias para referencias que no sean propiedad de los recursos y cuando sepa que el recurso sobrevivirá al objeto / alcance de referencia. Prefiera referencias y use punteros sin procesar cuando necesite nulabilidad o reiniciabilidad.
Si desea una referencia no propietaria de un recurso, pero no sabe si el recurso sobrevivirá al objeto que hace referencia a él, empaque el recurso en ay
shared_ptr
use unweak_ptr
- puede probar si el padreshared_ptr
está vivolock
, lo que devuelve unshared_ptr
que no es nulo si el recurso todavía existe. Si desea probar si el recurso está muerto, úseloexpired
. Los dos pueden sonar similares, pero son muy diferentes frente a la ejecución concurrente, ya queexpired
solo garantiza su valor de retorno para esa declaración única. Una prueba aparentemente inocente comoEs una posible condición de carrera.
fuente
shared_ptr
puntero no propietario. aweak_ptr
...shared_array<T>
es una alternativa ashared_ptr<T[]>
no hacerloshared_ptr<vector<T>>
: no puede crecer.Decidir qué puntero inteligente usar es una cuestión de propiedad . Cuando se trata de la gestión de recursos, el objeto A posee el objeto B si está en control de la vida útil del objeto B. Por ejemplo, las variables miembro son propiedad de sus respectivos objetos porque la vida útil de las variables miembro está vinculada a la vida útil del objeto. Elige punteros inteligentes en función de cómo se posee el objeto.
Tenga en cuenta que la propiedad en un sistema de software es independiente de la propiedad, ya que podríamos pensar fuera de él. Por ejemplo, una persona puede "ser propietaria" de su hogar, pero eso no significa necesariamente que un
Person
objeto tenga control sobre la vida útil de unHouse
objeto. Combinar estos conceptos del mundo real con los conceptos de software es una forma segura de programarse en un hoyo.Si tiene la propiedad exclusiva del objeto, úselo
std::unique_ptr<T>
.Si ha compartido la propiedad del objeto ...
- Si no hay ciclos de propiedad, use
std::shared_ptr<T>
.- Si hay ciclos, defina una "dirección" y úsela
std::shared_ptr<T>
en una dirección ystd::weak_ptr<T>
en la otra.Si el objeto lo posee, pero existe la posibilidad de no tener un propietario, utilice punteros normales
T*
(por ejemplo, punteros principales).Si el objeto es tuyo (o de lo contrario tiene existencia garantizada), usa referencias
T&
.Advertencia: Tenga en cuenta los costos de los punteros inteligentes. En entornos con memoria o rendimiento limitado, podría ser beneficioso usar punteros normales con un esquema más manual para administrar la memoria.
Los costos:
std::shared_ptr
tiene la sobrecarga de un incremento de recuento de referencia en la copia, más una disminución en la destrucción seguida de una verificación de recuento de 0 con la eliminación del objeto retenido. Dependiendo de la implementación, esto puede inflar su código y causar problemas de rendimiento.Ejemplos:
Un árbol binario no posee su padre, pero la existencia de un árbol implica la existencia de su padre (o
nullptr
de raíz), por lo que utiliza un puntero normal. Un árbol binario (con semántica de valor) tiene la propiedad exclusiva de sus hijos, por lo que sonstd::unique_ptr
.Aquí, el nodo de lista posee sus listas siguiente y anterior, por lo que definimos una dirección y la usamos
shared_ptr
para siguiente yweak_ptr
anterior para romper el ciclo.fuente
shared_ptr<BinaryTree>
para los niños yweak_ptr<BinaryTree>
para la relación con los padres.Utilizar
unique_ptr<T>
todo el tiempo, excepto cuando necesite un recuento de referencias, en cuyo caso useshared_ptr<T>
(y en casos muy raros,weak_ptr<T>
para evitar ciclos de referencia). En casi todos los casos, la propiedad única transferible está bien.Punteros sin procesar: buenos solo si necesita retornos covariantes, puntería no propietaria que puede suceder. De lo contrario, no son terriblemente útiles.
Punteros de matriz:
unique_ptr
tiene una especialización para laT[]
que se solicita automáticamentedelete[]
el resultado, por lo que puede hacerlo con seguridad,unique_ptr<int[]> p(new int[42]);
por ejemplo.shared_ptr
aún necesitaría un eliminador personalizado, pero no necesitaría un puntero de matriz compartido o único especializado. Por supuesto, tales cosas generalmente se reemplazan mejor destd::vector
todos modos. Desafortunadamenteshared_ptr
, no proporciona una función de acceso a matriz, por lo que aún tendría que llamar manualmenteget()
, perounique_ptr<T[]>
proporciona enoperator[]
lugar deoperator*
yoperator->
. En cualquier caso, tienes que verificarte los límites. Esto hace que sea unshared_ptr
poco menos fácil de usar, aunque podría decirse que es una ventaja genérica y que ninguna dependencia de Boost haceunique_ptr
yshared_ptr
los ganadores nuevamente.Indicadores de alcance: se vuelven irrelevantes
unique_ptr
, al igual queauto_ptr
.Realmente no hay nada más. En C ++ 03 sin semántica de movimiento, esta situación era muy complicada, pero en C ++ 11 el consejo es muy simple.
Todavía hay usos para otros punteros inteligentes, como
intrusive_ptr
ointerprocess_ptr
. Sin embargo, son muy específicos y completamente innecesarios en el caso general.fuente
std::unique_ptr<T[]>
proporciona enoperator[]
lugar deoperator*
yoperator->
. Sin embargo, es cierto que aún debes hacer un control de ti mismo.Casos de cuándo usar
unique_ptr
:Casos de cuándo usar
shared_ptr
:Casos de cuándo usar
weak_ptr
:Siéntase libre de editar y agregar más
fuente