2010-03-01 5 views
10

¿Qué tipo de trucos se puede utilizar para reducir al mínimo la carga de trabajo de la aplicación de las clases pImpl?Ejecución pImpl con una cantidad mínima de código

Cabecera:

class Foo { 
    struct Impl; 
    boost::scoped_ptr<Impl> self; 
public: 
    Foo(int arg); 
    ~Foo(); 
    // Public member functions go here 
}; 

Implementación:

struct Foo::Impl { 
    Impl(int arg): something(arg) {} 
    // All data members and private functions go here 
}; 

Foo::Foo(int arg): self(new Impl(arg)) {} 
Foo::~Foo() {} 

// Foo's public functions go here (and they refer to data as self->something) 

¿Cómo mejoraría esto, el uso de Boost, posiblemente herencia, CRTP u otros trucos para evitar en la medida código repetitivo como es posible? El rendimiento del tiempo de ejecución no es un problema.

+3

realidad, que no implementa el lenguaje pimpl ya sea porque no se haya solucionado el constructor de copia y la asignación copia ' operador = 'en Foo. –

+1

No suelo necesitar copyability (y scoped_ptr proporciona de forma predeterminada noncopyability), pero usted tiene un punto, la creación de esas funciones también podría automatizarse. – Tronic

Respuesta

5

Implementación de pimpl de Loki puede ser una buena respuesta. Véase también una DDJ Article en esto.

+0

Supongo que esto es tan bueno como se pone ... – Tronic

1

Es posible, pero una implementación ingenua no es lo que quiere.

El problema es que las plantillas están generalmente entre líneas, la implementación ingenua sería:

template <class Object> 
class Pimpl 
{ 
public: 
    explicit Pimpl(Object* obj): mObject(obj) {} 
    ~Pimpl() { delete mObject; } 

    // either deep copy or no copy 
private: 
    Object* mObject; 
}; 

Ahora el problema es que no desea Object a ser conocido en el archivo de cabecera en general (no para binario compatibilidad, pero para la administración de la dependencia). Y si Object no se conoce, entonces no puede implementar la Destructor, Copy Constructor y Assignment Operator directamente ...

El problema está lejos de ser imposible de resolver sin embargo! Impulso de hecho tiene que resolver para el shared_ptr.

La idea es pasar un segundo elemento en el constructor, que se encargará de liberar la memoria del primero, y que se le proporcionará una buena implementación predeterminada.

Esto funciona con una indirección, por supuesto.

namespace detail { 
    template <class Object> 
    struct Deleter { virtual void do(Object*) = 0; }; 
} 

template <class Object> 
class Pimpl 
{ 
public: 
    typedef detail::Deleter<Object> deleter_type; 
    typedef boost::shared_ptr<deleter_type> deleter_pointer; 

    Pimpl(std::auto_ptr<Object> obj, deleter_pointer del); 
    ~Pimpl(); 
    Pimpl(const Pimpl&); 
    Pimpl& operator(const Pimpl&); 

private: 
    Object* mObject; 
    deleter_pointer mDeleter; 
}; 

Es un lenguaje clásico en C++, añadir otro nivel de indirección :)

+0

¿Es este el puntero inteligente para mantener la implementación? No amplíe scoped_ptr y shared_ptr ya admiten tipos incompletos? – visitor

Cuestiones relacionadas