4

から派生したBaseクラスとクラスがあります。DerivedBase

他のクラスでは、 type のメンバーが必要ですshared_ptr<Base>

Baseそのような直接コピーはサブクラスを除外するため、型を直接使用することはできません。

Baseただし、オブジェクトが変更される可能性を排除したいので、構築時にオブジェクト (またはサブクラス)を「コピー」したいと考えています。

これに対処する古典的な方法は、すべてのサブクラスが実装できるクラスに仮想メンバー関数clone()を配置することです。Everyは、それ自体の「コピー」を返すだけです。たとえば、を返します。BaseBaseclone()Derivedmake_shared<Derived>(*this)

このアプローチの問題点は、この関数Baseを実装するために のすべての新しいサブクラスが必要になることです。clone()それぞれのコードclone()はどちらかというとボイラープレートであり、常に繰り返すのはやや不自然に思えます。

C++11以降、これを行うためのより良い方法はありますか?

4

1 に答える 1

5

プレーンな C++ でこれを行うことは常に可能でした:

struct base
{
    virtual ~base () {}
    virtual base* clone () = 0;  
    virtual void foo () = 0;
};

template <typename T>
struct base_impl : base
{
    T* clone () { return new T (*static_cast<T*> (this)); }
};

struct derived : base_impl<derived>
{ 
    void foo () { ... }
};

struct derived2 : base_impl<derived2>
{
    void foo () { ...}
};

これは C++11 で改善できます: を使用できますunique_ptr<base>(ただし、共変の戻り値の型は失われますbase_impl) friend T

この場合、これはあまり柔軟ではないことに同意しますが、

  • 多くの階層化された階層はあまり一般的ではありません
  • クローン機能はあまり役に立ちません
  • それにもかかわらず、設計は拡張可能であり、複製を超えています。定型コードを自動化する方法としてテンプレートを使用することは、たとえば. ATL と WTL。「不思議に繰り返されるテンプレート パターン」で検索してください。

別の解決策。これはおそらくさまざまな方法で改善される可能性がありますが、次の 2 つのクローン関数を使用することは避けられないと思います。

struct base
{
    std::unique_ptr<base> clone () { return std::unique_ptr<base> (do_clone ()); }

private:
    virtual base *do_clone () = 0;
};

template <typename T>
struct base_impl : base
{
    std::unique_ptr<T> clone () 
    {
        return std::unique_ptr<T> (static_cast<T*> (do_clone ()));
    }    

private:
    base *do_clone () { return new T (*static_cast<T*> (this)); }
};
于 2012-12-16T00:18:59.047 に答える