12

GUIコンポーネントに使用される複合パターンの実装があります。

class CObject {
private:

  CObject * m_pParent;  
  CObjectContainer * m_pChildren;

  void private_foo() {
    this->foo();
    //Calls private_foo for each child in container.
    m_pChildren->foo();
  }

public:
  virtual void foo() {
    //empty for base class
  }

  virtual CObject * duplicate() {
    //Do duplication code
    return new CObject(*this);
  }

  virtual CObject * detach() {
    //Remove this object (along with it's children)
    //from current tree.
    m_pParent->RemoveChild(this);
    m_pParent = nullptr;
    return this;
  }
}

class CSpecificObject : public CObject {
public:
  virtual void foo() {
    //Specific code for this class
  }

  virtual CSpecificObject * duplicate() {
    //Overload, but the code only calls diferent constructor
    return new CSpecificObject(*this);
  }

  virtual CSpecificObject * detach() {
    //Note the code is identical.
    m_pParent->RemoveChild(this);
    m_pParent = nullptr;
    return this;
  }
}

残念ながら、継承されたクラスの数が急速に増加し、重複したコード (この例では detach() メソッドのみ) が頭を悩ませています。

detach() メソッドをきれいに実装し、戻り値の型をそれが呼び出されたオブジェクトと同じに保つ方法はありますか?

CRTP について考えていましたが、動的ポリモーフィズムとコンパイル時のポリモーフィズムを維持する方法が思いつきません。

template <Child>
class CObject {
private:
  ...
  Child * detach() {
    m_pParent->RemoveChild(this);
    m_pParent = nullptr;
    return static_cast<Child*>(this);
  }
  ...
}

//Array of CObject* pointers is no longer possible.
4

3 に答える 3

7

1 レベルの抽象化を追加できます。

class CObjectBase
{
    public:
        // Other methods...
        virtual CObjectBase* detach() = 0;
        virtual CObjectBase* duplicate() const = 0;
};

template <typename Child>
class CObject : public CObjectBase
{
    public:
        // ...
        Child* duplicate() const
        {
            return new Child(*static_cast<Child*>(this));
        }

        Child* detach()
        {
            m_pParent->RemoveChild(this);
            m_pParent = nullptr;
            return static_cast<Child*>(this); // Cast needed here (inherent to CRTP)
        }
        std::vector<CObjectBase*> children; // Array possible now
        // ...
};

class MyObject : public CObject<MyObject>
{
    // ...
};

自然言語では、すべてのオブジェクトのインターフェース ( CObjectBase) には、その子孫 ( CObject<Child>) の部分的な実装があり、この部分的な実装を継承するだけでよく、複製されるコードの量が減ります。

于 2012-12-13T20:33:54.683 に答える
1

detach()スニペットだけでは、配信された型へのポインタを返す必要がある理由は不明です。

配信されたタイプをdetach()返すことを利用するには、とにかく配信されたタイプへの参照を使用して呼び出す必要があります。このような:

CSpecificObject* specific_object = new SpecificObject();
// ...
specific_object->detach()->method_declared_in_specific_object();

ただし、これは、デタッチが無効な場合でも機能する同等のものに置き換えることができます。

specific_object->detach();
specific_object->method_declared_in_specific_object();

基本型への参照がある場合、detach()戻り型を利用することはできません。

CObject* specific_object = new SpecificObject();
//...
// !!! Won't compile:
specific_object->detach()->method_declared_in_specific_object(); 

このため、実装しようとしているアプローチの利点は何であるかは不明です。

側面は、duplicate()方法が臭いということではありません。配信されたクラスが上書きしない場合は中断しますが、親クラスのデフォルトの実装を使用します。高レベルの設計に問題があることを示している可能性があります。

于 2012-12-14T10:02:09.847 に答える
1

CRTPを考えていたのですが、動的ポリモーフィズムをコンパイル時のポリモーフィズムとともに維持する方法が思いつきません。

CRTP スタイルの基本クラスを使用して、特定のインターフェイスにデフォルトの仮想実装を提供することで、それらを混在させることができます。

したがって、CRTP ベースの実装 (追加の「ポリシー」テンプレート パラメーターで構成されている可能性があります) を集約する可能性があり、継承されたクラスの特定の動作をオーバーライドすることもできます。

Microsoft のATL ライブラリはこれをよく使用します。STTCL ステート マシン ライブラリでもこの手法を使用しています。

于 2012-12-13T20:35:02.733 に答える