2

次のようなトークン クラスがあります。

class Token
{
 public:
   typedef enum { STRTOK, INTTOK } Type;
   virtual bool IsA(Type) = 0;
}

class IntTok : public Token
{
   int data;
 public:
   bool IsA(Type t) { return (t == INTTOK); }
   int GetData() { return data; }
}

IntTok newToken;
if ( newToken.IsA(Token::INTTOK )
{
  //blah blah
}

したがって、本質的には、すべてのサブクラスを Token クラスで定義する必要があります。サブクラスが非常に少なく、それらが変更されるとは想像できないため、それほど悪い結果にはなりません。それでも、動的キャストを使用してサブクラスを識別するよりも、醜く、ぎこちなく、「正しく」ありません。でも:

IntTok newToken;
IntTok* tmpTokenTest = dynamic_cast<IntTok*>(&newToken);
if ( tmpTokenTest != NULL )
{
  //blah blah
}

また、かなり不器用です。特に、ネストされた大きな if でそれらをつなぎ合わせる必要がある場合。

それで、あなたはどちらを使いますか?この問題の別の解決策はありますか?

注:とにかく、それぞれのデータを取得するにはそれらをキャストする必要があることはわかっていますが、

  1. 関数を使用する直前までキャストしないので、すっきりと感じられます。
  2. 私はそれらのデータを使用するよりもはるかに頻繁にそれらの型をテストします。

注 2:上記のコードでは、これらのトークンがリンクされたリストでもあることは示されていません。これにより、テンプレート化が困難になります( aToken<int>が a を指す場合Token<string>など)。これが、最初に親として Token クラスが必要な理由です。

4

5 に答える 5

3

来客パターンですね。

class TokenVisitor {
public:
    virtual ~TokenVisitor() { }
    virtual void visit(IntTok&) = 0;
    virtual void visit(StrTok&) = 0;
};

class Token {
 public:
   virtual void accept(TokenVisitor &v) = 0;
};

class IntTok : public Token {
   int data;
 public:
   virtual void accept(TokenVisitor &v) {
       v.visit(*this);
   }
   int GetData() { return data; }
};

次に、訪問者インターフェースを実装して呼び出すだけです

token->accept(myVisitor);

制御は訪問者に与えられ、訪問者は適切なアクションを実行できます。変数をローカルで適切な型にする必要がある場合 - ただし、ダウンキャストを回避することはほとんどありません。しかし、仮想関数を使用して特定の実装に制御を駆動することは、多くの場合、それを解決するための良い方法だと思います。

于 2009-02-22T19:17:46.170 に答える
3

代わりに仮想関数を使用して、必要なことを実行してください。これの代わりに:

if(newToken.IsA(Token::INTTOK))
{
    // do stuff with ((IntTok*)&newToken)->GetData()
}

これを行う:

class Token
{
public:
    ...
    virtual void doTypeDependentStuff() {}  // empty default implementation
}

class IntTok : public Token
{
public:
    ...
    void doTypeDependent()
    {
        // do stuff with data
    }
}
于 2009-02-22T18:47:26.070 に答える
2

Boost::Variant を使用することをお勧めします。これは、基本的に複数の型の結合です (バリアント型のオブジェクトは、Ti ( 1 <= i <= n ) 型の任意のオブジェクトを保持できます)。

これを使用すると、継承を使用する必要がなくなります。

詳細については、そこを参照してください。

于 2009-02-23T08:13:07.943 に答える
0

したがって、本質的には、すべてのサブクラスを Token クラスで定義する必要があります

理由を説明できますか?

本当にキャストする必要がありますか?ポリモーフィック関数を使用できます。

または、テンプレート化された Token クラス (一部はデフォルトの動作) を作成し、残りの部分を特化することもできます。

于 2009-02-22T18:47:56.193 に答える
0

RTTIを使用するバージョンを使用する可能性が高くなりますが、それは厄介なものです.

新しい C++ コンパイラ (実際にはサポートされていなかった VC 6.0 で最後に試しました) は typeid 演算子を想定していたので、完全な動的キャストは必要ありませんでしたか?

于 2009-02-22T18:50:31.960 に答える