2011-12-05 14 views
6

que suelen utilizar un impulso :: scoped_ptr para de pimpl (por una razón, porque entonces no consigo sorpresas si me olvido de lidiar con el constructor de copia)pimpl-idioma en la plantilla; qué puntero inteligente?

Con las plantillas sin embargo no puedo sólo hay que poner el destructor de el archivo cpp donde la impl está completamente definida para cumplir los requisitos del destructor de scoped_ptr. Funciona de todos modos, pero no estoy seguro de si está garantizado para funcionar o simplemente por casualidad. ¿Hay alguna 'mejor práctica' o estándar? ¿Scoped_ptr es el mejor puntero inteligente para pimpls en clases que no se pueden copiar?

template <class T> class C { 
public: 
    C(){} 
    ~C(){} 
private: 
    boost::scoped_ptr<T> pimpl_; 
}; 
+4

Este tipo de implementación PIMPL no tiene ningún sentido porque para crear una instancia de la plantilla C, debe conocer el tipo T. PIMPL, por otro lado, oculta completamente el equivalente de T del usuario. –

+0

@VladLazarenko Hmm, pensé impulso :: scoped_ptr funciona tan bien en las clases predeclared. En este caso, depende si T está definido o predeclamado. La instanciación de este scoped_ptr estaría oculta en la implementación (pimpl_ (new T()). –

+0

@DavidFeurle: En realidad, para que esta plantilla funcione, el tamaño de 'T', así como su interfaz, debe ser expuesto, porque los "clientes" necesitan crear una instancia de la plantilla. Por ejemplo, ¿a dónde llama 'nueva T()'? No puede ocultar eso en el archivo "cpp" porque debe estar en la plantilla. Por lo tanto, no es PIMPL. –

Respuesta

1

boost::shared_ptr no requiere una definición completa que no sea en el punto de instanciación en — el constructor, en el caso de un pimpl . boost::shared_ptr es no es apropiado para el idioma pimpl, sin embargo, ya que da una semántica muy inesperada (semántica de referencia para asignación o copia); si realmente desea la complejidad adicional de un puntero inteligente , boost::scoped_ptr sería más adecuado (pero requiere una definición completa en el punto en que su destructor es instanciado).

Con respecto a las plantillas, no tiene sentido utilizar el idioma pimpl para los detalles de implementación desde el encabezado. En ausencia de export, , se deben incluir todos los detalles de implementación de una plantilla de clase donde se use la plantilla, por lo que la motivación detrás de la terminología pimpl deja de existir.

13

Sucede que Herb Sutter comenzó a escribir sus GotWs nuevamente después de un largo tiempo. Uno de los primeros nuevos está relacionado con "Compilation Firewalls".

Es posible que desee echar un vistazo a:

GotW #100: Compilation Firewalls (Difficulty: 6/10)

y

GotW #101: Compilation Firewalls, Part 2 (Difficulty: 8/10)

+0

Me encantan sus libros. Parece que respondió mi pregunta sobre qué tipo de puntero usar usando unique_ptr. – odinthenerd

+0

haciendo la respuesta: ' unique_ptr pimpl' – sehe

+1

Parece comprado en la mayor complejidad es mejor la filosofía de la hierba. Todas sus soluciones basadas en estándares introducen una complejidad adicional sin ganancia real. –

2

Dos años después entiendo la situación mucho mejor, en interés de mantener las respuestas de desbordamiento de pila relevantes y actualizadas aquí es cómo respondería la pregunta hoy.

La premisa de mi pregunta original es algo defectuosa. La razón para usar el idioma pimpl es ocultar los detalles de implementación del compilador. Esto se hace almacenando la implementación a través de un puntero opaco (puntero a un tipo de datos declarado pero no definido). Esto puede reducir en gran medida la cantidad de encabezados necesarios para otras unidades de compilación que interactúan con la clase y así acelerar el tiempo de compilación. En el caso de la plantilla en mi pregunta, se requiere que el tipo T sea completamente conocido en el momento de la creación de instancias, lo que en la práctica requiere que el tipo impl esté completamente definido dondequiera que se use C<ImplType>, haciendo esto claramente no un ejemplo del idioma pimpl en el sentido clásico del término.

Existen otras razones para mantener datos de clases a través de un puntero privado, por ejemplo, permite una fácil implementación de movimiento e intercambio de no-throw y también es bueno si su clase necesita cumplir una fuerte garantía de excepción (ver copia y swap idiom What is the copy-and-swap idiom?). Por otro lado, agrega una capa de direccionamiento indirecto (que a menudo resulta en una falta de caché) en cada acceso al impl y una asignación/desasignación de un montón al crear y destruir el impl.Estas pueden ser sanciones de rendimiento sustanciales, por lo tanto, esta solución no debe considerarse una solución milagrosa.

Si puede usar C++ 11, se debe usar std :: unique_ptr en lugar de boost :: scoped_ptr.

Cuestiones relacionadas