6

次のclone関数を定義する必要があるクラスがいくつかあります。

struct Base
{
  virtual Base * clone() const = 0;
};

struct A : public Base
{
    Base * clone() const {
      return new A(*this);
  }
};

struct B : public Base
{
    Base * clone() const {
      return new B(*this);
  }
};

struct X : public Base2
{
    Base2 * clone() const {
      return new X(*this);
  }
};

この冗長なコードを回避するために、クローン可能なミックスインでこれを実行しようとしています。

template <typename BASE, typename TYPE>
class CloneableMixin
{
public:
  BASE*clone() const {
    return new TYPE( dynamic_cast<const TYPE &>(*this) );
  }
};

struct A : public Base, public CloneableMixin<Base, A>
{
};

new TYPE(*this)ただし、 fromはタイプCloneableMixinであるため、これは機能しません。*thisCloneableMixin<BASE, TYPE>

更新:CloneableMixindynamic_castを正しいタイプに。しかし今、私は別の問題を抱えています:CloneableMixin::clone正常にオーバーライドされないBase::cloneので、コンパイラはAが抽象型であると報告します。

virtual継承を巧妙に使用するCloneableMixin::cloneことでオーバーライドできますBase::cloneか?これに使用する必要のあるマクロはありますか?

この冗長なコードすべてを回避する方法を知っていますか?

4

2 に答える 2

6

仮想継承の巧妙な使用により、CloneableMixin::clone が Base::clone をオーバーライドできるようになりますか?

は から派生していないため、ポリモーフィックまたは非表示CloneableMixin<Base,Derived>のいずれかのメソッドをオーバーライドすることはできません。BaseCloneableMixin<Base,Derived>Base

一方、あなたCloneableMixin<Base,Derived> から派生したBase 場合、それをミックスインにする必要はなくなります。

class Derived : public CloneableMixin<Base,Derived> {....};

を継承しBaseます。

したがって、例のニーズには、ここに示すソリューションで十分です。

#include <iostream>

// cloner v1.0
template <class Base, class Derived>
struct cloner : Base
{
    Base *clone() const override {
        return new Derived( dynamic_cast<const Derived &>(*this) );
    }
    ~cloner() override {};
};

struct Base
{
    virtual Base * clone() const = 0;
    Base() {
        std::cout << "Base()" << std::endl;
    }
    virtual ~Base() {
        std::cout << "~Base()" << std::endl;
    }
};


struct A : cloner<Base,A> 
{
    A() {
        std::cout << "A()" << std::endl;
    }
    ~A() override {
        std::cout << "~A()" << std::endl;
    }
};

int main()
{
    A a;
    Base * pb = a.clone();
    delete pb;
}

(C++11 ではなく C++03 標準にコンパイルする場合は、overrideキーワードの出現箇所を単純に削除できます。)

この解決策は、いくつかのより現実的なクラス階層に分解されます。たとえば、次のTemplate Method Patternの図では:

#include <iostream>
#include <memory>

using namespace std;

// cloner v1.0
template<class B, class D>
struct cloner : B
{
    B *clone() const override {
        return new D(dynamic_cast<D const&>(*this));
    }
    ~cloner() override {}       
};

/*  Abstract base class `abstract` keeps the state for all derivatives
    and has some pure virtual methods. It has some non-default
    constructors. 
*/
struct abstract 
{
    virtual ~abstract() {
        cout << "~abstract()" << endl;
    }
    int get_state() const {
        return _state;
    }
    void run() {
        cout << "abstract::run()" << endl;
        a_root_method();
        another_root_method();
    }
    virtual void a_root_method() = 0;
    virtual void another_root_method() = 0;
    virtual abstract * clone() const = 0;

protected:

    abstract()
    : _state(0) {
        cout << "abstract(): state = " << get_state() << endl;
    }
    explicit abstract(int state) : _state(state) {
        cout << "abstract(" << state << ") : state = " 
        << get_state() << endl;
    }   
    int _state;
};

/*  Concrete class `concrete` inherits `abstract`
    and implements the pure virtual methods.
    It echoes the constructors of `abstract`. Since `concrete`
    is concrete, it requires cloneability. 
*/
struct concrete : cloner<abstract,concrete> 
{   
    concrete() { 
        cout << "concrete(): state = " << get_state() << endl;
    }
    explicit concrete(int state) : abstract(state) {  //<- Barf!
        cout << "concrete(" << state << ") : state = " 
            << get_state() << endl;
    }
    ~concrete() override {
        cout << "~concrete()" << endl;
    }
    void a_root_method() override {
        ++_state; 
        cout << "concrete::a_root_method() : state = " 
            << get_state() << endl;
    }
    void another_root_method() override {
        --_state;
        cout << "concrete::another_root_method() : state = " 
            << get_state() << endl;
    }       
};

int main(int argc, char **argv)
{
    concrete c1;
    unique_ptr<abstract> pr(new concrete(c1));
    pr->a_root_method();
    pr->another_root_method();
    unique_ptr<abstract> pr1(pr->clone());
    pr1->a_root_method();
    return 0;
}

これをビルドしようとすると、コンパイラーはコンストラクターの初期化abstract(state)時にconcrete(Barf! コメントで)、次のようにエラーを出します。

error: type 'abstract' is not a direct or virtual base of 'concrete'

またはその趣旨の言葉。実際、 の直接の基数はconcreteis not abstract butcloner<abstract,concrete>です。ただし、コンストラクターを次のように書き換えることはできません。

/*Plan B*/ explicit concrete(int state) : cloner<abstract,concrete>(state){....}

のようなコンストラクタがないため

cloner<abstract,concrete>::cloner<abstract,concrete>(int)

しかし、コンパイラの診断は修正を示唆しています。これは、仮想継承が役立つ場合があります。の仮想基地abstractになる必要があり、これは事実上「の名誉直接基地」を意味し、仮想基地を作成するだけでそれを達成できます。concreteconcreteBcloner<B,D>

// cloner v1.1
template<class B, class D>
struct cloner : virtual B
{
    B *clone() const override {
        return new D(dynamic_cast<D const&>(*this));
    }
    ~cloner() override {}       
};

これで、クリーンなビルドと出力が得られました。

abstract(): state = 0
concrete(): state = 0
concrete::a_root_method() : state = 1
concrete::another_root_method() : state = 0
concrete::a_root_method() : state = 1
~concrete()
~abstract()
~concrete()
~abstract()
~concrete()
~abstract()

原則として仮想継承に注意し、少なくともアーキテクチャ上の論理的根拠がある場合にのみその使用を保留する十分な理由があります。

この問題を仮想継承なしで処理したい場合は、任意の の任意コンストラクターcloner<B,D>をエコーする のコンストラクターがあることを何らかの方法で確認する必要があります。次に、対応するコンストラクターは 、引数が何であれ、その直接ベースを初期化できます。BBDcloner<B,D>

これは C++03 の夢物語ですが、C++11 の可変個引数テンプレート パラメーターの魔法を使えば簡単です。

// cloner v1.2
template<class B, class D>
struct cloner : B
{
    B *clone() const override {
        return new D(dynamic_cast<D const&>(*this));
    }
    ~cloner() override {}
    // "All purpose constructor"
    template<typename... Args>
    explicit cloner(Args... args)
    : B(args...){}  
};

これにより、コンストラクターを として書き換えることができ、正しいビルドと実行可能ファイルが再び得られます。concrete/*Plan B*/

于 2013-02-26T13:31:05.233 に答える
0

Cloneable mixin のインスタンス化中、派生クラスはまだ不完全な型です。次のように、ことわざのような間接的なレベルを追加してみることができます。

template 
<
    typename Derived
>
class Cloneable 
:    
    private CloneableBase
{
public:
    Derived* clone() const
    {
        return static_cast<Derived*>(this->do_clone());
    }

private:
    virtual Cloneable* do_clone() const
    {
        return new Derived(static_cast<const Derived&>(*this));
    }
};

class CloneableBase
{
public:
    CloneableBase* clone() const
    {
        return do_clone();
    }

private:
    virtual CloneableBase* do_clone() const=0;
};

class MyClass: public Cloneable<MyClass>;
于 2012-05-04T06:45:44.847 に答える