2010-09-07 8 views
7

que he visto que una forma útil para escribir un método que devuelve un clon de impulso :: shared_ptr es hacerCómo hacer un método clon usando shared_ptr y heredando de enable_shared_from_this

class A 
{ 
public: 
    shared_ptr<A> Clone() const 
    { 
    return(shared_ptr<A>(CloneImpl())); 
    } 
protected: 
    virtual A* CloneImpl() const 
    { 
    return(new A(*this)); 
    } 
}; 

class B : public A 
{ 
public: 
    shared_ptr<B> Clone() const 
    { 
    return(shared_ptr<B>(CloneImpl())); 
    } 
protected: 
    virtual B* CloneImpl() const 
    { 
    return(new B(*this)); 
    } 
}; 

Esto permite el uso de covarianza con el puntero regular sin dejar de envolverlo en la seguridad de un puntero inteligente. Mi problema es que mi clase B necesita heredar de boost :: enable_shared_from_this porque justo después de la construcción necesita registrarse en una clase separada, pasando un puntero compartido a sí mismo. Tengo un método Create que envuelve la construcción y el registro para garantizar que siempre ocurran juntos. La implementación del método de clonación anterior no puede manejar este requisito, sin embargo. El registro no puede ocurrir en CloneImpl ya que todavía no existe shared_ptr que "posee" el objeto, impidiendo una llamada a shared_from_this(), y si esta lógica no está en la función virtual, shared_ptr apunta a B, no sabe acerca de las necesidades de registro de B cuando es clonado ¿Cuál es la mejor manera de manejar este problema?

+0

¿Compila este código? Parece que B :: Clone() oculta A :: Clon(). –

+0

Este ejemplo compilado para mí. Creo que es útil que B :: Clone() oculte A :: Clone() para que obtenga el tipo de devolución correcto. – user334066

Respuesta

7

Puesto que ya está implementando la pública interfaz de covarianza a sí mismo a través de las no virtuales Clone() funciones, se puede considerar el abandono de la covarianza para los CloneImpl() funciones.

Si sólo necesita shared_ptr y nunca el puntero del crudo, por lo que entonces podría hacer:

class X 
{ 
public: 
    shared_ptr<X> Clone() const 
    { 
    return CloneImpl(); 
    } 
private: 
    virtual shared_ptr<X> CloneImpl() const 
    { 
    return(shared_ptr<X>(new X(*this))); 
    } 
}; 

class Y : public X 
{ 
public: 
    shared_ptr<Y> Clone() const 
    { 
    return(static_pointer_cast<Y, X>(CloneImpl())); // no need for dynamic_pointer_cast 
    } 
private: 
    virtual shared_ptr<X> CloneImpl() const 
    { 
    return shared_ptr<Y>(new Y(*this)); 
    } 
}; 

CloneImpl() siempre se obtendrá una shared_ptr<Base> y ahora usted puede registrar su objeto dentro de la función B::CloneImpl() y devolver el shared_ptr Registerd .

+0

Gracias, creo que esto resuelve el problema muy bien. De hecho, incluso ignorando el requisito de registro, ¿este método de clonación no parece incluso mejor que el que enumeré, que es el ejemplo que siempre veo al clonar con punteros inteligentes? Esta versión parece ofrecer la misma funcionalidad al tiempo que elimina cualquier posible uso indebido del puntero normal que se crea en el CloneImpl que enumeré. – user334066

+0

@ user334066 - Supongo que depende. El ejemplo que publiqué se puede encontrar en los archivos de la lista de correo de boost. Su función de Clon permite el uso interno de la clase del puntero sin formato que puede tener sentido de vez en cuando. –

Cuestiones relacionadas