c++ - shared_ptr - unique pointer
punteros inteligentes+"esto" considerado daƱino? (8)
Cuando está utilizando una clase de puntero inteligente, tiene razón en que es peligroso exponer directamente " this
". Hay algunas clases de punteros relacionadas con boost::shared_ptr<T>
que pueden ser de utilidad:
-
boost::enable_shared_from_this<T>
- Proporciona la capacidad de hacer que un objeto devuelva un puntero compartido que utiliza los mismos datos de conteo de referencia que un puntero compartido existente para el objeto
-
boost::weak_ptr<T>
- Funciona de la mano con punteros compartidos, pero no contiene una referencia al objeto. Si todos los punteros compartidos desaparecen y el objeto se libera, un puntero débil podrá decir que el objeto ya no existe y le devolverá
NULL
lugar de un puntero a la memoria no válida. Puede utilizar punteros débiles para obtener punteros compartidos a un objeto válido contado de referencia.
- Funciona de la mano con punteros compartidos, pero no contiene una referencia al objeto. Si todos los punteros compartidos desaparecen y el objeto se libera, un puntero débil podrá decir que el objeto ya no existe y le devolverá
Ninguno de estos es infalible, por supuesto, pero al menos harán que su código sea más estable y seguro al tiempo que proporciona el acceso adecuado y el recuento de referencias para sus objetos.
En un proyecto de C ++ que utiliza punteros inteligentes, como boost::shared_ptr
, ¿cuál es una buena filosofía de diseño con respecto al uso de " this
"?
Considere eso:
Es peligroso almacenar el puntero sin procesar contenido en cualquier puntero inteligente para su uso posterior. Ha renunciado al control de eliminación de objetos y confía en que el puntero inteligente lo haga en el momento correcto.
Los miembros de clase no estáticos usan intrínsecamente
this
puntero. Es un puntero sin formato y no se puede cambiar.
Si alguna vez almacena this
en otra variable o la paso a otra función que podría almacenarla para más adelante o enlazarla en una devolución de llamada, estoy creando errores que se introducen cuando alguien decide hacer un puntero compartido a mi clase.
Dado que, ¿cuándo es apropiado para mí usar explícitamente this
puntero? ¿Hay paradigmas de diseño que puedan prevenir errores relacionados con esto?
Otra razón para pasar esto es si desea mantener un registro central de todos los objetos. En el constructor, un objeto llama a un método estático del registro con esto. Es útil para varios mecanismos de publicación / suscripción, o cuando no desea que el registro necesite conocer qué objetos / clases hay en el sistema.
Personalmente, me gusta utilizar este puntero al acceder a las variables miembro de la clase. Por ejemplo:
void foo::bar ()
{
this->some_var += 7;
}
Es solo una cuestión inofensiva de estilo. A algunas personas les gusta, algunas personas no.
Pero usar este puntero para cualquier otra cosa es probable que cause problemas. Si realmente necesita hacer cosas elegantes con él, realmente debería reconsiderar su diseño. Una vez vi un código que, en el constructor de una clase, ¡asignaba este puntero a otro puntero almacenado en otro lugar! Eso es una locura, y no puedo pensar en una razón para hacer eso. Todo el código fue un gran desastre, por cierto.
¿Puedes decirnos qué es exactamente lo que quieres hacer con el puntero?
Si bien no tengo una respuesta general o un modismo, hay boost::enable_shared_from_this
. Le permite obtener un shared_ptr administrando un objeto que ya está administrado por shared_ptr. Como en una función miembro no se hace referencia a los que administran shared_ptr, enable_shared_ptr le permite obtener una instancia de shared_ptr y pasarla cuando necesite pasar este puntero.
Pero esto no resolverá la cuestión de pasar this
desde dentro del constructor, ya que en ese momento, no hay shared_ptr administrando su objeto todavía.
Si necesita usar this
, simplemente úselo explícitamente. Los punteros inteligentes solo envuelven punteros de los objetos que poseen, ya sea de manera exclusiva ( unique_ptr
) o de manera compartida ( shared_ptr
).
Un ejemplo de uso correcto es return *this;
en funciones como operador ++ () y operador << ().
Otra opción es utilizar punteros inteligentes intrusivos y tener en cuenta el recuento de referencias dentro del objeto en sí, no los punteros. Esto requiere un poco más de trabajo, pero en realidad es más eficiente y fácil de controlar.
Pregunta equivocada
En un proyecto de C ++ que utiliza punteros inteligentes
El problema no tiene nada que ver con los punteros inteligentes en realidad. Solo se trata de la propiedad.
Los punteros inteligentes son solo herramientas
No cambian nada WRT el concepto de propiedad, esp. la necesidad de tener una propiedad bien definida en su programa , el hecho de que la propiedad puede ser transferida voluntariamente, pero no puede ser tomada por un cliente.
Debe comprender que los punteros inteligentes (también bloqueos y otros objetos RAII) representan un valor y una relación WRT este valor al mismo tiempo. Un shared_ptr
es una referencia a un objeto y establece una relación: el objeto no debe destruirse antes de este shared_ptr
, y cuando este shared_ptr
se destruye, si es el último alias de este objeto, el objeto debe destruirse inmediatamente. ( unique_ptr
se puede ver como un caso especial de shared_ptr
donde hay aliasing cero por definición, por lo que el unique_ptr
es siempre el último alias de un objeto).
Por qué deberías usar punteros inteligentes
Se recomienda utilizar punteros inteligentes porque expresan mucho con solo declaraciones de variables y funciones.
Los punteros inteligentes solo pueden expresar un diseño bien definido, no eliminan la necesidad de definir la propiedad. Por el contrario, la recolección de basura elimina la necesidad de definir quién es responsable de la desasignación de memoria. (Pero no elimine la necesidad de definir quién es responsable de la limpieza de otros recursos).
Incluso en lenguajes recolectados de basura que no son puramente funcionales, debe aclarar la propiedad: no desea sobrescribir el valor de un objeto si otros componentes aún necesitan el valor anterior. Esto es especialmente cierto en Java, donde el concepto de propiedad de la estructura de datos mutable es extremadamente importante en los programas con subprocesos.
¿Qué hay de los indicadores sin formato?
El uso de un puntero sin formato no significa que no haya propiedad. Simplemente no está descrito por una declaración de variable. Puede describirse en comentarios, en sus documentos de diseño, etc.
Es por eso que muchos programadores de C ++ consideran que el uso de punteros crudos en lugar del puntero inteligente adecuado es inferior : porque es menos expresivo (he evitado los términos "bueno" y "malo" a propósito). Creo que el kernel de Linux sería más legible con unos pocos objetos C ++ para expresar relaciones.
Puede implementar un diseño específico con o sin punteros inteligentes. La implementación que utiliza un puntero inteligente de forma apropiada será considerada superior por muchos programadores de C ++.
Tu verdadera pregunta
En un proyecto de C ++, ¿cuál es una buena filosofía de diseño con respecto al uso de "esto"?
Eso es terriblemente vago.
Es peligroso almacenar el puntero sin procesar para usarlo más adelante.
¿Por qué necesitas un puntero para usarlo más tarde?
Ha renunciado al control de eliminación de objetos y confía en el componente responsable para hacerlo en el momento adecuado.
De hecho, algún componente es responsable de la duración de la variable. No puede asumir la responsabilidad: tiene que ser transferido.
Si alguna vez almacena esto en otra variable o lo paso a otra función que podría almacenarlo para más tarde o enlazarlo en una devolución de llamada, estoy creando errores que se introducen cuando alguien decide usar mi clase.
Obviamente, dado que la persona que llama no está informada de que la función ocultará un puntero y lo usará más tarde sin el control de la persona que llama, usted está creando errores.
La solución es obviamente:
- transferir la responsabilidad de manejar la vida del objeto a la función
- asegúrese de que el puntero solo se guarde y use bajo el control de la persona que llama
Solo en el primer caso, puede terminar con un puntero inteligente en la implementación de la clase.
La fuente de tu problema
Creo que su problema es que está tratando de complicar las cosas usando punteros inteligentes. Los punteros inteligentes son herramientas para facilitar las cosas, no más. Si los indicadores inteligentes complican su especificación, vuelva a pensar su especificación en términos de cosas más simples.
No intente introducir punteros inteligentes como solución antes de tener un problema.
Solo introduzca punteros inteligentes para resolver un problema específico bien definido. Debido a que no describe un problema específico bien definido, no es posible analizar una solución específica (que involucre indicadores inteligentes o no).