2016-06-13 2 views
3

У меня проблема с клонируемыми абстрактными классами и уникальными указателями. Предположим, что у меня есть следующий Клонируемый абстрактный базовый классКлонируемая иерархия классов и unique_ptr

class Base 
{ 
    public: 
     virtual void doSomething()=0; 
     virtual std::unique_ptr<Base> clone() const=0; 
} 

и производный абстрактный класс, который обеспечивает дополнительный способ

class Derived : public Base 
{ 
    public: 
     virtual void doSomething()=0; 
     virtual void doSomethingMore()=0; 
     virtual std::unique_ptr<Base> clone() const=0; 
} 

Это позволяет определить новые классы, которые хранят полиморфный объект иерархии базы по составу , Например

class Composed 
{ 
    public: 
     Composed(Base const &base_) : basePtr(base_.clone()) {} 

    private: 
     std::unique_ptr<Base> basePtr; 
} 

В этом случае я должен быть в состоянии хранить объект типа Derived в Состоящем, без нарезки методы, которые производные добавляют WRT Base. Однако я хотел бы определить другой объект, который хранит полиморфный объект, который наследует Derived, рассматривая его как объект типа Derived. Используя ту же структуру, что и выше

class ComposedDerived 
{ 
    public: 
     ComposedDerived(Derived const &derived_) : derivedPtr(derived_.clone()) {} 

    private: 
     std::unique_ptr<Derived> derivedPtr; 
} 

Очевидно, я получаю ошибку компиляции, так как метод клонирования производных возвращает std::unique_ptr<Base>. С другой стороны, если я изменить определение производного следующего

class Derived : public Base 
{ 
    public: 
     virtual void doSomething()=0; 
     virtual void doSomethingMore()=0; 
     virtual std::unique_ptr<Derived> clone() const=0; 
} 

В этом случае компилятор выдает следующую ошибку: invalid covariant return type for ‘virtual std::unique_ptr<Derived> Derived::clone() const.

Есть ли способ для компилятора, чтобы понять, что std::unique_ptr<Derived> на самом деле может быть использован в качестве std::unique_ptr<Base> через полиморфизм и не спорьте о типе возвращаемого производного класса clone метода?

+2

Как насчет 'производныйPtr (static_cast (производный_.clone(). Release()))'? –

+0

Это кажется разумным способом сделать это. Не могли бы вы объяснить '.release()'? Из того, что я понимаю, 'output_.clone()' возвращает объект 'std :: unique_ptr '. '.release()' let this unique_ptr освобождает право собственности на объект 'Derived', который был создан в куче и возвращает объект' Base * '. Наконец, я навел этот указатель на указатель 'Derived *', который используется для инициализации 'производногоPtr'. Это верно? –

ответ

2

Используйте NVI (не виртуальный интерфейс идиома) для метода clone() следующим образом:

class Base 
{ 
    public: 
     virtual void doSomething()=0; 

     std::unique_ptr<Base> clone() const { 
      return cloneImpl(); 
     } 

    private: 
     virtual std::unique_ptr<Base> cloneImpl() const=0; 
}; 

class Derived : public Base 
{ 
    public: 
     virtual void doSomething()=0; 
     virtual void doSomethingMore()=0; 

     std::unique_ptr<Derived> clone() const { 
      return std::unique_ptr<Derived>(static_cast<Derived*>(cloneImpl().release())); 
     } 
}; 

Вы даже можете добавить больше безопасности и удобства «переопределение» метод clone() в подклассы следующим образом:

class Base 
{ 
    public: 
     virtual void doSomething()=0; 

     std::unique_ptr<Base> clone() const { 
      return checkedClone<Base>(); 
     } 

    protected: 
     template<class T> 
     std::unique_ptr<T> checkedClone() const { 
      auto p = cloneImpl(); 
      assert(typeid(*p) == typeid(*this) && "subclass doesn't properly override cloneImpl()"); 
      assert(nullptr != dynamic_cast<T*>(p.get())); 
      return std::unique_ptr<T>(static_cast<T*>(p.release())); 
     } 

    private: 
     virtual std::unique_ptr<Base> cloneImpl() const=0; 
}; 

class Derived : public Base 
{ 
    public: 
     virtual void doSomething()=0; 
     virtual void doSomethingMore()=0; 

     std::unique_ptr<Derived> clone() const { 
      return checkedClone<Derived>(); 
     } 
}; 
+0

Это интересно. Поэтому, если я правильно понимаю, мне просто нужно было бы реализовать метод cloneImpl() const 'virtual std :: unique_ptr cloneImpl() в конкретных классах, наследуемых от' Base' или 'Derived'. Это правильно? –

+0

Да. Но если вам также нужно будет легко клонировать объект конкретного класса, вы также можете «переопределить» функцию 'clone()', аналогичную тому, как это было сделано в 'Derived'. – Leon

+0

Итак, в классе 'Derived' я могу одновременно использовать метод stone :: unique_ptr clone() const и метод stone :: unique_ptr clone() const'. Это потому, что метод 'clone()' в классе «Base» больше не является абстрактным, поэтому я могу его переопределить? –

1

насчет эксплуатирующего ковариантном возврата:

class Base { 
public: 
    std::unique_ptr<Base> clone() const { 
     return std::unique_ptr<Base>(cloneImpl()); 
    } 
    virtual ~Base(); 
private: 
    virtual Base* cloneImpl() const; 
}; 

class Derived : public Base { 
public: 
    std::unique_ptr<Derived> clone() const { 
     return std::unique_ptr<Derived>(cloneImpl()); 
    } 
    ~Derived() override; 
private: 
    // Covariant return: 
    Derived* cloneImpl() const override; 
}; 
+0

При таком подходе мне пришлось бы реализовать методы 'cloneImpl()' в каждом классе, наследуемые от 'Base' или' Derived'. Это правильно? Каковы преимущества и недостатки NVI, предложенные компанией @Leon? Спасибо за вашу помощь. –

+0

Про является то, что вам не нужен static_cast. – ysdx

+0

Представьте, что в классе 'Derived' я хотел иметь метод' std :: unique_ptr clone() const' и метод stone :: unique_ptr clone() const'. Как я могу достичь этого с помощью этой техники? –

Смежные вопросы