Confusión sobre el impulso :: shared_ptr

Mi pregunta gira en torno a si debo o no exponer mi uso de boost :: shared_ptr desde mi interfaz y si debo o no exponer los punteros en bruto o las referencias de mi interfaz.

Considere el caso de una Persona que tiene un Empleador . El empleador mantiene internamente a todos sus empleados en un vector< shared_ptr > . Debido a esto, ¿las mejores prácticas dictan que cualquier interfaz que incluya a Person debería ser una persona envuelta shared_ptr?

Por ejemplo, son todos o solo algunos de estos ok:

 Person Employeer::getPresidentCopy(); Person& Employeer::getPresidentRef(); Person* Employeer::getPresidentRawPtr(); shared_ptr Employeer::getPresidentSharedPtr(); 

O por ejemplo:

 void Employeer::hireByCopy(Person p); void Employeer::hireByRef(Person& p); void Employeer::hireByRawPtr(Person* p); void Employeer::hireBySharedPtr(shared_ptr p); 

Si luego deseo cambiar la implementación para usar johns_very_own_shared_ptr en lugar de la variedad boost, ¿estoy atrapado en la implementación anterior?

Por otro lado, si expongo los punteros o referencias en bruto desde la interfaz, ¿me arriesgo a que alguien elimine la memoria de shared_ptr? ¿O me arriesgo a que shared_ptr se elimine y anule mi referencia?


Vea mi nueva pregunta para un ejemplo que involucra esto.

Por ejemplo, son todos o solo algunos de estos ok:

Depende de lo que estés tratando de lograr. ¿Por qué el vector mantiene shared_ptr s en lugar de simplemente almacenar los Person s por valor? (¿Y has considerado boost::ptr_vector ?)

También debes considerar que tal vez lo que realmente deberías repartir es un valor weak_ptr .

Si luego deseo cambiar la implementación para usar johns_very_own_shared_ptr en lugar de la variedad boost, ¿estoy atrapado en la implementación anterior?

Bastante, pero no es imposible de arreglar. (Sospecho que en C ++ 0x, el uso liberal de la palabra clave auto hará que esto sea más fácil de manejar, ya que no tendrá que modificar tanto el código de llamada, incluso si no usó typedef s.) Pero entonces, ¿por qué querrías hacer eso?

Por otro lado, si expongo los punteros o referencias en bruto desde la interfaz, ¿me arriesgo a que alguien elimine la memoria de shared_ptr?

Sí, pero ese no es tu problema. Las personas pueden extraer un puntero sin shared_ptr y delete también. Pero si desea evitar hacer cosas innecesariamente inseguras, no devuelva aquí los punteros en bruto. Las referencias son mucho mejores porque nadie se imagina que se supone que deben delete &reference_received_from_api; . ( Espero que sí , de todos modos ^^ ;;;;)

Yo introduciría un typedef y me aislaría contra los cambios. Algo como esto:

 typedef std::shared_ptr PersonPtr; PersonPtr Employeer::getPresident() const; 

Coloco typedefs como este en un encabezado (solo uno) junto con declaraciones hacia adelante. Esto hace que sea fácil de cambiar si alguna vez quisiera.

No tiene que entregar shared_ptr, pero si distribuye punteros en bruto, corre el riesgo de que persista algún puntero en bruto después de que el objeto haya sido destruido.

Con consecuencias fatales.

Por lo tanto, la distribución de referencias generalmente es correcta (si el código del cliente toma la dirección, entonces no es más culpa suya que si el código del cliente toma la dirección de * shared_ptr), pero los punteros sin procesar, piense primero.

Salud y salud,

No debería dar punteros sin shared_ptr usuario cuando utiliza shared_ptr s. El usuario podría borrarlo, lo que causará doble eliminación.

Para ocultar el uso de boost:shared_ptr puede usar typedef para ocultar el tipo real, y usar este nuevo tipo en su lugar.

 typedef boost::shared_ptr Person_sptr; 

La única razón para entregar un shared_ptr aquí es si la vida útil de la referencia de objeto devuelta no está vinculada directamente a la vida útil de su residencia en el vector.

Si desea que alguien pueda acceder a la Person después de que deje de ser un Employee , shared_ptr sería apropiado. Diga si está moviendo la Person al vector para un Employer diferente.

Trabajo en un proyecto de tamaño moderado que se enlaza en varias bibliotecas. Algunas de esas bibliotecas tienen sus propios subsistemas de administración de memoria (APR, MFC) y realmente me molestan. Ya sea que su visión del mundo sea buena o mala, es completamente diferente a la de todos los demás y requiere un poco más de código de lo que de otro modo necesitarían.

Además, esas bibliotecas hacen que el intercambio de malloc o new con jemalloc o el recolector de basura Boehm-Demers-Weiser sea mucho más difícil ( en Windows ya es lo suficientemente difícil ).

Utilizo mucho los punteros compartidos en mi propio código, pero prefiero no decirles a otros cómo administrar su memoria. En su lugar, distribuya los objetos siempre que sea posible (dejando que el usuario de la biblioteca decida cuándo y cómo asignar la memoria), y cuando no sea posible, realice una de las siguientes acciones:

  1. repartir punteros en bruto (más una promesa de que los punteros se pueden delete d o una función Destroy() para llamar a desasignar los objetos)
  2. acepte una función o un argumento similar a un asignador STL para que pueda conectarse a cualquier cosa que el usuario utilice para la gestión de la memoria (no dude en utilizar de forma predeterminada el new y std::allocator )
  3. haga que los usuarios de su biblioteca le asignen buffers de memoria ( como std::vector s )

Usar este tipo de biblioteca no tiene que ser desagradable:

 // situation (1) from above std::shared_ptr foo(Library_factory::get_Foo(), Library_factory::deallocate); // situation (2) above (declaration on next line is in a header file) template > Foo* library_function_call(); boost::shared_ptr foo = library_function_call(); // situation (3) above, need to fill a buffer of ten objects std::vector foo_buffer(10); fill_buffer(&foo_buffer[0], foo_buffer.size());