2016-06-13 82 views
3

我有一個可克隆抽象類和唯一指針的問題。假設我有以下可克隆抽象基類Clonable class hierarchy and 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; 
} 

這樣,我應該能夠存儲組成派生類型的對象,不切片派生增加WRT基地的方法。但是,我想定義另一個對象,該對象存儲從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

怎麼樣'derivedPtr(static_cast (derived_.clone()。release()))? –

+0

這似乎是一個聰明的做法。你能解釋一下'.release()'嗎?據我所知,'derived_.clone()'返回一個'std :: unique_ptr對象。 '.release()'讓這個unique_ptr釋放已經在堆中創建的Derived'對象的所有權,並返回一個'Base *'對象。最後,我將這個指針轉換爲一個'Derived *'指針,用於初始化'derivedPtr'。那是對的嗎? –

回答

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

這很有趣。因此,如果我理解正確,我只需要在從'Base'或'Derived'繼承的具體類中實現'virtual std :: unique_ptr cloneImpl()const'方法。是對的嗎? –

+0

是的。但是,如果您還需要能夠輕鬆地克隆具體類的對象,那麼您也可以「覆蓋」clone()函數,類似於在Derived中完成的操作。 – Leon

+0

因此,在派生類中,我可以同時使用'std :: unique_ptr clone()const'方法和'std :: unique_ptr clone()const'方法。那是因爲'Base'類中的'clone()'方法不再是抽象的,因此我可以覆蓋它? –

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

使用這種方法,我將不得不在每個繼承自Base或Derived的類中實現'cloneImpl()'方法。是對的嗎? @Leon提出的NVI有什麼優點和缺點?感謝您的幫助 –

+0

親是你不需要static_cast。 – ysdx

+0

想象一下,在派生類中,我想同時使用'std :: unique_ptr clone()const'方法和'std :: unique_ptr clone()const'方法。我如何使用這種技術來實現這一點? –