c++ c++11 shared-ptr pimpl-idiom unique-ptr

c++ - Debería usar shared_ptr o unique_ptr



c++11 shared-ptr (4)

He estado haciendo algunos objetos usando el idioma pimpl, pero no estoy seguro si usar shared_ptr o unique_ptr .

Definitivamente unique_ptr o scoped_ptr .

Pimpl no es un patrón, sino una expresión idiomática, que trata sobre la dependencia del tiempo de compilación y la compatibilidad binaria. No debería afectar la semántica de los objetos, especialmente con respecto a su comportamiento de copia.

Puede usar cualquier tipo de puntero inteligente que desee bajo el capó, pero esos 2 garantizan que no compartirá accidentalmente la implementación entre dos objetos distintos, ya que requieren una decisión consciente sobre la implementación del constructor de copias y el operador de asignación.

Sin embargo, en realidad estos objetos no se están copiando, ya que los cambios afectan a todas las copias, por lo que me preguntaba si quizás usar shared_ptr y permitir copias es algo así como algo anti patrón o malo.

No es un antipatrón, de hecho, es un patrón: Aliasing. Ya lo usas, en C ++, con punteros y referencias. shared_ptr ofrece una medida adicional de "seguridad" para evitar referencias muertas, a costa de una complejidad adicional y nuevos problemas (tenga cuidado con los ciclos que crean pérdidas de memoria).

No relacionado con Pimpl

Entiendo que unique_ptr es más eficiente, pero esto no es un problema para mí, ya que estos objetos son relativamente pesados ​​de todos modos, por lo que el costo de shared_ptr en unique_ptr es relativamente menor.

Si puede factorizar algún estado, es posible que desee echar un vistazo al patrón Flyweight .

He estado creando algunos objetos con el idioma pimpl, pero no estoy seguro si usar std::shared_ptr o std::unique_ptr .

Entiendo que std::unique_ptr es más eficiente, pero esto no es un problema para mí, ya que estos objetos son relativamente pesados ​​de todos modos, por lo que el costo de std::shared_ptr sobre std::unique_ptr es relativamente menor.

Actualmente voy con std::shared_ptr solo por la flexibilidad adicional. Por ejemplo, el uso de std::shared_ptr me permite almacenar estos objetos en un hashmap para un acceso rápido mientras sigo siendo capaz de devolver copias de estos objetos a las personas que llaman (ya que creo que cualquier iterador o referencia puede volverse inválido rápidamente).

Sin embargo, estos objetos en cierto modo no se están copiando, ya que los cambios afectan a todas las copias, por lo que me preguntaba si quizás usar std::shared_ptr y permitir copias es algún tipo de antipatrón o malo.

¿Es esto correcto?


Cuando utiliza un shared_ptr (por ejemplo, en un contenedor, luego busca esto y lo devuelve por valor ), no está causando una copia del objeto al que apunta, simplemente una copia del puntero con un recuento de referencia.

Esto significa que si modifica el objeto subyacente desde varios puntos, entonces usted afecta los cambios en la misma instancia . Esto es exactamente para lo que está diseñado, ¡por lo que no es un antipatrón !

Al pasar un shared_ptr (como dicen los comentarios), es mejor pasar por referencia constante y copiar (incrementando el recuento de referencias) donde sea necesario. En cuanto a devolución, caso por caso.


Sí, por favor úsalas. En pocas palabras, shared_ptr es una implementación de puntero inteligente. unique_ptr es una implementación de puntero automático:


Si usa shared_ptr , no es realmente el idioma clásico de los pimpl (a menos que tome pasos adicionales). Pero la verdadera pregunta es por qué quieres usar un puntero inteligente para empezar; está muy claro dónde debe ocurrir la delete , y no hay ningún problema con la seguridad de la excepción u otra de la que preocuparse. Como máximo, un puntero inteligente le ahorrará una o dos líneas de código. Y el único que tiene la semántica correcta es boost::scoped_ptr , y no creo que funcione en este caso. (IIRC, requiere un tipo completo para poder crear una instancia, pero podría estar equivocado).

Un aspecto importante del lenguaje de los pimpl es que su uso debe ser transparente para el cliente; la clase debería comportarse exactamente como si se implementara de forma clásica. Esto significa, ya sea inhibir la copia y la asignación o implementar una copia profunda, a menos que la clase sea inmutable (no funciones miembro no constantes). Ninguno de los punteros inteligentes habituales implementan una copia profunda; podría implementar uno, por supuesto, pero probablemente requeriría un tipo completo cada vez que se produzca la copia, lo que significa que todavía tendría que proporcionar un constructor de copia definido por el usuario y un operador de asignación (ya que no pueden estar en línea). Dado esto, probablemente no vale la pena molestarse con el puntero inteligente.

Una excepción es si los objetos son inmutables. En este caso, no importa si la copia es profunda o no, y shared_ptr maneja la situación por completo.