我想能够比较表达式的语法树。基类Expr
具有用于具体的子类来覆盖纯虚compare
方法:C++动态对象类比较
class Expr {
public:
virtual bool compare(const Expr *other) const = 0;
};
作为一个例子,说NumExpr
和AddExpr
是用于表示文字整数表达的两种具体子类和二进制分别添加表达。的第一件事,每一个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
对象之间进行动态比较的方法 ?
使用visitor design pattern确实不是解决了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(据我所知);例如,这里是比较表达式的样本访问者类:我们还在使用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();
}
};
注(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) {}
...
};
然后我们可以使用static_cast
和flavor
场,以避免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) {
result = expr->flavor == NUM_EXPR && static_cast<NumExpr *>(expr)->num == e->num;
}
...
};
这种解决方案看起来像我只是复制了RTTI正在做的事情。
查找**双派**。 –
或访客设计模式。 –
如果“底层表达式是相同的”并且在其他情况下返回false,比较函数返回true是正确的。 – user1055604