我希望能够比较表达式的语法树。基类Expr
具有纯虚拟compare
方法,用于覆盖具体子类:
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;
};
具体的表达式子类然后使用 double dispatch 来调用相应的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) {}
...
};
然后我们可以使用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正在做的事情。
答案 0 :(得分:1)
假设您在编译时不知道任何一方的动态类型(例如静态类型与动态类型相同),并且您实际上想要通过指针比较两个Expr
对象或者引用,那么你将不得不进行两次虚拟调用(双重调度)或使用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;
}
};
答案 1 :(得分:0)
可以使用typeid Operator
代替dynamic_cast
进行初始类型匹配。
答案 2 :(得分: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;
}
}
};