4

式の構文木を比較できるようにしたい。基本クラスには、具体的なサブクラスがオーバーライドするためExprの純粋仮想メソッドがあります。compare

class Expr {
public:
  virtual bool compare(const Expr *other) const = 0;
};

例として、とはNumExprAddExprそれぞれリテラル整数式と2進加算式を表す2つの具体的なサブクラスです。各compareメソッドが最初に行うことは、式が同じタイプであるdynamic_castことを確認するために使用することです。other

class NumExpr : public Expr {
  int num;
public:
  NumExpr(int n) : num(n) {}
  bool compare(const Expr *other) const {
    const NumExpr *e = dynamic_cast<const NumExpr*>(other);
    if (e == 0) return false;
    return num == e->num;
  }
};

class AddExpr : public Expr {
  Expr *left, *right;
public:
  AddExpr(Expr *l, Expr *r) : left(l), right(r) {}
  bool compare(const Expr *other) const {
    const AddExpr *e = dynamic_cast<const AddExpr*>(other);
    if (e == 0) return false;
    return left->compare(e->left) && right->compare(e->right);
  }
};

使用すると、いつも何か間違ったことをしているように感じます。dynamic_cast使用せずにオブジェクト間の動的比較を実行するためのより適切なアプローチはありますdynamic_castか?

ビジターデザインパターンを使用しても、RTTIの必要性は解決されませ(私が知る限り)。「式ビジター」の抽象基本クラスは、次のようになります。

class NumExpr;
class AddExpr;

class ExprVisitor {
public:
  virtual void visit(NumExpr *e) {}; // "do nothing" default
  virtual void visit(AddExpr *e) {};
};

式の基本クラスには、純粋仮想acceptメソッドが含まれています。

class Expr {
public:
  virtual void accept(ExprVisitor& v) = 0;
};

次に、具象式サブクラスは、ダブルディスパッチvisitを使用して適切なメソッドを呼び出します。

class NumExpr : public Expr {
public:
  int num;
  NumExpr(int n) : num(n) {}
  virtual void accept(ExprVisitor& v) {
    v.visit(this);
  };
};

class AddExpr : public Expr {
public:
  Expr *left, *right;
  AddExpr(Expr *l, Expr *r) : left(l), right(r) {}
  virtual void accept(ExprVisitor& v) {
    v.visit(this);
  };
};

最終的にこのメカニズムを使用して式の比較を実行するようになったときでも、RTTIを使用する必要があります(私が知る限り)。たとえば、式を比較するための訪問者クラスの例を次に示します。

class ExprCompareVisitor : public ExprVisitor {
  Expr *expr;
  bool result;
public:
  ExprCompareVisitor(Expr *e) : expr(e), result(false) {}
  bool getResult() const {return result;}

  virtual void visit(NumExpr *e) {
    NumExpr *other = dynamic_cast<NumExpr *>(expr);
    result = other != 0 && other->num == e->num;
  }

  virtual void visit(AddExpr *e) {
    AddExpr *other = dynamic_cast<AddExpr *>(expr);
    if (other == 0) return;

    ExprCompareVisitor vleft(other->left);
    e->left->accept(vleft);
    if (!vleft.getResult()) return;

    ExprCompareVisitor vright(other->right);
    e->right->accept(vright);
    result = vright.getResult();
  }
};

まだRTTIを使用していることに注意してください(dynamic_castこの場合)。

RTTIを本当に避けたい場合は、「独自のロール」を使用して、すべての具体的な表現フレーバーを識別するための一意の定数を作成できます。

enum ExprFlavor {
  NUM_EXPR, ADD_EXPR
};

class Expr {
public:
  const ExprFlavor flavor;
  Expr(ExprFlavor f) : flavor(f) {}
  ...
};

各具象タイプは、この定数を適切に設定します。

class NumExpr : public Expr {
public:
  int num;
  NumExpr(int n) : Expr(NUM_EXPR), num(n) {}
  ...
};

class AddExpr : public Expr {
public:
  Expr *left, *right;
  AddExpr(Expr *l, Expr *r) : Expr(ADD_EXPR), left(l), right(r) {}
  ...
};

次に、RTTIを回避するためstatic_castにフィールドを使用できます。flavor

class ExprCompareVisitor : public ExprVisitor {
  Expr *expr;
  bool result;
public:
  ExprCompareVisitor(Expr *e) : expr(e), result(false) {}
  bool getResult() const {return result;}

  virtual void visit(NumExpr *e) {                                                                
    result = expr->flavor == NUM_EXPR && static_cast<NumExpr *>(expr)->num == e->num;
  }
  ...
};

このソリューションは、RTTIが内部で行っていることを複製しているように見えます。

4

3 に答える 3

1

コンパイル時にどちらの側の動的型もわからず (たとえば、静的型は動的型と同じ)、実際にはExprポインターまたは参照によって 2 つのオブジェクトを比較したいと仮定すると、次のようになります。 2 つの仮想呼び出し (二重ディスパッチ) を行うか、 を使用する必要がありますdynamic_cast

次のようになります。

class Expr {
public:
  virtual bool compare(const Expr *other) const = 0;
  virtual bool compare(const NumExpr *other) const { return false; }
  virtual bool compare(const AddExpr *other) const {return false;}
};

class NumExpr : public Expr {
  int num;
public:
  explicit NumExpr(int n) : num(n) {}
  bool compare(const Expr *other) const {
    return other->compare(this);
  }
  bool compare(const NumExpr *other) const {
    return num == other->num;
  }
};
于 2012-12-28T19:39:23.963 に答える
0

RTTIを使用できます。

class NumExpr : public Expr {
  int num;
public:
   NumExpr(int n) : num(n) {}
bool compare(const Expr *other) const {
if ( typeid(*other) != typeid(*this) )
     return false;
 else {
     NumExpr *e = static_cast<NumExpr*>(other);
     return num == e->num;
  }
  }
};
于 2012-12-28T19:55:45.860 に答える
0

初期型マッチングのtypeid Operator代わりに使用できる があります。dynamic_cast

(ソース)

于 2012-12-28T19:49:04.900 に答える