tags:

views:

69

answers:

1

I have seen that a useful way to write a clone method that returns a boost::shared_ptr is to do

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));
  }
};

This allows the use of covariance with the regular pointer while still wrapping it in the safety of a smart pointer. My problem is my class B needs to inherit from boost::enable_shared_from_this because right after construction it needs to register itself with a separate class, passing a shared pointer to itself. I have a Create method that wraps construction and registration to make sure these always occur together. The above clone method implementation cannot handle this requirement, however. The registration cannot occur in CloneImpl since no shared_ptr yet exists "owning" the object, preventing a call to shared_from_this(), and if this logic is not in the virtual function then an shared_ptr that points to B, does not know about B's registration needs when cloned. What is the best way to handle this problem?

+2  A: 

Since you are already implementing the public interface covariance yourself via the non-virtual Clone() functions, you may consider abandoning the covariance for the CloneImpl() functions.

If you only need shared_ptr and never the raw pointer, so you could then do:

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() would always return a shared_ptr<Base> and now you could register your object inside the B::CloneImpl() function and return the registerd shared_ptr.

Martin
Thank you I believe this solves the problem very well. In fact, even ignoring the registration requirement, doesn't this clone method seem even better than the one I listed, which is the example I always see when cloning with smart pointers? This version seems to gives the same functionality while removing any possible misuse of the regular pointer that is created in the CloneImpl I listed.
@user334066 - I guess it depends. The example I posted can be found on the boost mailing list archives. Your Clone function allowed for class-internal use of the raw pointer which may make sense from time to time.
Martin