2016-08-18 4 views
2

私のプログラムは、2つの派生クラス、ChoiceNodeとOpponentNodeを持つ抽象クラスBaseNodeを持っています。私はChoiceNodeから呼び出された場合はOpponentNodeを返す必要があり、OpponentNodeから呼び出された場合はChoiceNodeを返す "returnOpposite"という純粋仮想関数をBaseNodeに書きたいと思います。 OpponentNode.h別の派生クラスの共変戻りタイプの関数を実装するには?

#include "BaseNode.h" 

class OpponentNode: public BaseNode { 
    ChoiceNode& returnOpposite(); 
} 

BaseNode.h

class BaseNode { 
    protected: 
     virtual BaseNode& returnOpposite() = 0; 
} 
ChoiceNode.h

#include "BaseNode.h" 

class ChoiceNode: public BaseNode { 
    OpponentNode& returnOpposite(); 
} 

ここに私の問題は、対戦相手/ ChoiceNodeはの知識を持つ必要があるということです通常は前方宣言を使用して解決される反対のクラスですが、compiのためには反対のクラスがBaseNodeと共変であることを認識するためには、クラスに関する文脈情報が必要です。

それは、この情報が適切なヘッダファイルを含めることによって提供されていることを私の理解です。しかし、これを行うと、何らかの循環依存性が生じます。 ChoiceNodeにはChoiceNodeを含める必要があるOpponentNodeを含める必要がありますが、ヘッダーガードがあれば、OpponentNodeはChoiceNodeクラス宣言を知らないようです。

この見た目のcatch-22をどのように解決すればよいですか?循環依存に関与せずにクラスについてのコンテキスト情報を提供する方法はありますか?

答えて

0

により、あなたが識別した循環依存関係の問題に、あなたは、真の共同バリアント戻り値の型を使用することはできません。

ただし、共変戻り値の型の導入前に働いていた技術を使用することができます。

まず、私は純粋仮想基底クラスを作る、virtual Node& returnOppositeImpl()にすべてのクラスで宣言を変更することをお勧めします。また、クラス階層の残りの部分に応じて、関数とすべての実装をprivateと宣言します。次に、各クラスreturnOppositeに非仮想保護(または公開)メソッドを宣言し、上で説明した署名を使用します。これらの関数定義は、お互いを陰にするため、共変種の戻り値の型を必要とせず、その実装をそれぞれの.cppファイルに配置することができます。実装は単純な静的または動的キャストで、returnOppositeImplメソッドを呼び出します。

シャドウイングを使用すると、呼び出しサイトで静的型に対応するreturnOppositeが常に呼び出されるため、正しい参照型になります。しかし、さまざまなNodeクラス間のリンケージに関する実際の知識は実装に隠されており、依存関係のループは解消されています。

ので、

class BaseNode { 
    private: 
     virtual Node& returnOppositeImpl() = 0; 
    protected: // Or public: 
     Node& returnOpposite() { 
      return returnOppositeImpl(); 
     } 
} 

ChoiceNode.hでBaseNode.hで一緒にすべての

を取ら

ChoiseNode.cppで
#include "BaseNode.h" 
class OpponentNode; 
class ChoiceNode: public BaseNode { 
    private: 
     virtual Node& returnOppositeImpl(); 
    protected: // or public: 
     OpponentNode& returnOpposite(); 
} 

#include "ChoiseNode.h" 
#include "OpponentNode.h" 
OpponentNode& ChoiseNode::returnOpposite() 
{ 
    // You can use static cast here, if Node doesn't have virtual 
    // members, and/or if you can guarantee further subclasses 
    // will properly always return an OpponentNode reference. 

    return dynamic_cast<OpponenentNode&>(returnOppositeImpl()); 
} 

OpponentNode.hとOpponentNode.cpp a ChoiseNodeの実装と似ています

+0

私はあなたのコードのフォーマットがうんざりしていると思いますか? –

+0

@KeithMありがとう。どういうわけか私は最後の近くでそれを台無しにしました。 –

+0

解決策を適用すると、循環依存関係の問題が解決しました。ありがとうございます。 –

1

方法の全ての2 overridablesは同じ戻り値の型を持たなければなりません。

そうでなければ、あなたのコードがすべてでコンパイルされません、この抽象メソッドvirtual Node& returnOpposite() = 0;は、派生クラスには実装を持ちません。

class BaseNode { 
    protected: 
     virtual BaseNode* returnOpposite() = 0; 
} 

class ChoiceNode: public BaseNode { 
    virtual BaseNode* returnOpposite() override; 
} 

class OpponentNode: public BaseNode { 
    virtual BaseNode* returnOpposite() override; 
} 

(感謝@DaveSコメント)更新:両方の2種類が(あなたのスケッチのように)共変であれば、その後、次はうまく動作します:

だからあなたのクラスは次のようになります。

class BaseNode { 
    protected: 
     virtual BaseNode* returnOpposite() = 0; 
} 

class OpponentNode; // forward declaration 

class ChoiceNode: public BaseNode { 
    virtual OpponentNode* returnOpposite() override; 
} 

class OpponentNode: public BaseNode { 
    virtual ChoiceNode* returnOpposite() override; 
} 

ここでは、参照ではなくインスタンスへのポインタを返すことに注意してください。あなたが発見したとして

+2

これは正しくありません。 C++では、共変種の戻り値の型が可能です。熟練のループがすべてのクラスを適切に含むことを妨げるようでなければ、それはうまくいくでしょう。 –

+0

問題は、 'BaseNode'の仮想関数が' BaseNode'の戻り値型を持たなければならないということです。その場合、オーバーライドの戻り値の型が 'BaseNode'の子孫であることは完全に正当です。 'ChoiceNode'と' OpponentNode'はすべて実際には 'BaseNode'です。 –

+0

タイプが確かに共変であるなら、私の悪い、BaseNodeの代わりのNode&はタイプミスです。 –

関連する問題