2

次の無効な C++ コードを検討してください。

#include <assert.h>

class NodeInterface {
public:
    virtual ~NodeInterface () {}

    virtual int f (const int& n) const = 0;
};

class ChildNodeInterface : public NodeInterface {
public:
    virtual ~ChildNodeInterface () {}
};

class ParentNodeInterface : public NodeInterface {
public:
    virtual ~ParentNodeInterface () {}
};

class ChildNode : public ChildNodeInterface {
public:
    virtual ~ChildNode () {}

    virtual int f (const int& n) const {
        return 2*n;
    }
};

class ParentNode : public ParentNodeInterface, private ChildNodeInterface {
public:
    explicit ParentNode () :
        mChild (new ChildNode ())
    {
    }

    virtual ~ParentNode () {}

    ChildNodeInterface* GetChildHandle () {
        return this;
    }

    virtual int f (const int& n) const {
        return 3*n;
    }

private:
    ChildNode* const mChild;

    // How do I specify that I would like to override ChildNodeInterface::f?
    virtual int f (const int& n) const { // On MSVC2010: C2535 member function already defined or declared
        return 1 + mChild->f (n);
    }
};

int main()
{
    ParentNode parent;
    assert (parent.f (2) == 6);
    ChildNode node;
    assert (node.f (2) == 4);
    ChildNodeInterface* child (parent.GetChildHandle ());
    assert (child->f (2) == 5);
    return 0;
}

の実装の上にいくつかの追加機能を追加できるParentNodeように、個人的に を のように見せることが私の目標です。の単純さによって示されるように、偽装したハンドルとして効果的に扱うことができます。明らかに、から継承を繰り返さなければ問題はありません。したがって、オーバーライドを簡単に明確にすることができます。これは、次の正しい例で示されています。ChildNodeChildNodeChildNodeInterfaceParentNodeChildNodeGetChildHandleParentNodeNodeInterface

#include <assert.h>

class ChildNodeInterface {
public:
    virtual ~ChildNodeInterface () {}

    virtual int ChildMethod (const int& n) const = 0;
};

class ParentNodeInterface {
public:
    virtual ~ParentNodeInterface () {}

    virtual int ParentMethod (const int& n) const = 0;
};

class ChildNode : public ChildNodeInterface {
public:
    virtual ~ChildNode () {}

    virtual int ChildMethod (const int& n) const {
        return 2*n;
    }
};

class ParentNode : public ParentNodeInterface, private ChildNodeInterface {
public:
    explicit ParentNode () :
        mChild (new ChildNode ()),
        mValue (1)
    {
    }

    ChildNodeInterface* GetChildHandle () {
        return this;
    }

    virtual int ParentMethod (const int& n) const {
        return 3*n;
    }

private:
    ChildNode* const mChild;
    const int mValue;

    virtual int ChildMethod (const int& n) const {
        return mValue + mChild->ChildMethod (n);
    }
};

int main()
{
    ParentNode parent;
    assert (parent.ParentMethod (2) == 6);
    ChildNode node;
    assert (node.ChildMethod (2) == 4);
    ChildNodeInterface* child (parent.GetChildHandle ());
    assert (child->ChildMethod (2) == 5);
    return 0;
}

ただし、ParentNodeInterfaceとのChildNodeInterface両方が から継承する特殊なケースでNodeInterfaceは、あいまいさが生じます。の主張から明らかなようにmain、私は の仮想継承を目指していませんNodeInterfaceNodeInterface::finの真に異なる実装を持つことが私の意図ParentNodeです。ParentNodeInterface::fの実装とChildNodeInterface::finをどのように(可能であれば)区別できるのだろうかParentNode

4

2 に答える 2

1

あなたのデザインは間違っていると思います。あなたはChildNodeInterfaceを継承していて、ParentNodeクラスにChildNodeメンバーがいますか?

継承されたChildNodeInterfaceでは、ChildNodeクラスが継承ツリーにないため、f関数の実装を使用できません。

同じシグニチャを持つ関数を2回再定義することは、C++では許可されていません。したがって、同じ仮想関数の2つの実装を作成することはできません。

したがって、2つのオプションがあります。

  1. [最良]継承を正しい方法で使用して、クラス設計を再定義してみてください。ダイヤモンドの継承は、多くの場合、悪い設計が原因です。
  2. [最速]ChildNodeInterfaceから継承を削除し、ParentNode :: f()関数でmChild-> f()を呼び出して、子の動作を親に追加することをお勧めします。

    virtual int f(const int&n)const {return 3 * n + mChild-> f(n); }しかし、それがあなたが望む行動であるかどうかはわかりません。

于 2012-08-23T07:56:16.023 に答える
1

ChildNodeInterfaceとはまったく関係がないことに注意してくださいChildNode::f()

しかし、あなたの問題を解決するには (私は思う)、 から個人的に継承するのではなく、その型を継承してオーバーライドChildNodeInterfaceするヘルパー型を使用します。 これらのオブジェクトの 1 つへのポインターを維持できます (または、その型の単純な古いメンバーを持つことができます) 。ChildNodeChildNode::f()ParentNodeParentNode::GetChildHandle()

// the helper class
class ParentNode;
class ParentsChildNodeInterface : public ChildNode {
public:
    virtual int f (const int& n) const;
};

class ParentNode : public ParentNodeInterface {

public:
    explicit ParentNode () :
        mChild (new ParentsChildNodeInterface ())
    {
    }

    virtual ~ParentNode () {}

    ChildNodeInterface* GetChildHandle () {
        return mChild;
    }

    virtual int f (const int& n) const {
        return 3*n;
    }

private:
    ParentsChildNodeInterface* const mChild;
};

// the new, special f() for children of parents...
int ParentsChildNodeInterface::f (const int& n) const {
    return 1 + ChildNode::f (n);
}

ParentNode投稿されているように、ヘルパー クラスはオブジェクトの状態にアクセスする必要はありません。しかし、必要があれば、ヘルパー クラスをfriendof にすることもできますParentNode

于 2012-08-23T08:16:45.263 に答える