收录日期:2020/10/28 08:15:35 时间:2010-09-07 02:35:17 标签:c++,shared-ptr

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?

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.