2016-06-13 12 views
3

クローン可能な抽象クラスと一意のポインタに問題があります。私はこの組成物によってベース階層の多型オブジェクトを格納する新しいクラスを定義することができ、以下のクローン可能抽象基底クラスをClonableクラス階層と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>のcloneメソッドので、コンパイル・エラーが発生します。この場合

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 * 'オブジェクトを返します。最後に、このポインタを 'derivedPtr'を初期化するために使用される' Derived * 'ポインタにキャストします。あれは正しいですか? –

答えて

2

clone()メソッドのNVI(非仮想インターフェースイディオム)を使用してください:

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

はい。しかし、コンクリートクラスのオブジェクトを簡単に複製できるようにする必要がある場合は、 'Derived'でのやり方と同様に' clone() '関数を"オーバーライド "することもできます。 – Leon

+0

したがって、 'Derived'クラスでは、' 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

このアプローチでは、 'class'または' Derived'のいずれかを継承する各クラスに 'cloneImpl()'メソッドを実装する必要があります。そうですか? @ Leonによって提案されたNVIへの賛否両論は何ですか?あなたの助けをありがとう –

+0

プロはあなたがstatic_castを必要としないことです。 – ysdx

+0

'Derived'クラスで、' std :: unique_ptr clone()const'メソッドと 'std :: unique_ptr clone()const'メソッドの両方を使いたいとします。どのように私はこの技術を使用して達成することができますか? –

関連する問題