Как сделать метод клонирования, используя shared_ptr и наследуя от enable_shared_from_this - PullRequest
7 голосов
/ 07 сентября 2010

Я видел, что полезным способом написать метод клона, который возвращает boost :: shared_ptr, является выполнение

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

Это позволяет использовать ковариацию с обычным указателем, в то же время сохраняя его в безопасности интеллектуального указателя. Моя проблема в том, что мой класс B должен унаследовать от boost :: enable_shared_from_this, потому что сразу после построения ему нужно зарегистрировать себя в отдельном классе, передав общий указатель на себя. У меня есть метод Create, который оборачивает конструкцию и регистрацию, чтобы они всегда происходили вместе. Однако приведенная выше реализация метода клона не может удовлетворить это требование. Регистрация не может происходить в CloneImpl, так как еще не существует shared_ptr, «владеющего» объектом, предотвращающего вызов shared_from_this (), и если эта логика отсутствует в виртуальной функции, то shared_ptr, который указывает на B, не знает о потребностях B в регистрации когда клонировали. Как лучше всего решить эту проблему?

1 Ответ

7 голосов
/ 07 сентября 2010

Поскольку вы уже сами реализуете ковариацию интерфейса public с помощью не виртуальных функций Clone(), вы можете отказаться от ковариации для функций CloneImpl().

Если вам нужен только shared_ptr, а не необработанный указатель, то вы можете сделать:

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() всегда будет возвращать shared_ptr<Base>, и теперь вы можете зарегистрировать свой объект внутриB::CloneImpl() и вернуть зарегистрированный shared_ptr.

...