2012-12-28 66 views
4

我想能够比较表达式的语法树。基类Expr具有用于具体的子类来覆盖纯虚compare方法:C++动态对象类比较

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

作为一个例子,说NumExprAddExpr是用于表示文字整数表达的两种具体子类和二进制分别添加表达。的第一件事,每一个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_castflavor场,以避免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正在做的事情。

+1

查找**双派**。 –

+0

或访客设计模式。 –

+0

如果“底层表达式是相同的”并且在其他情况下返回false,比较函数返回true是正确的。 – user1055604

回答

1

假设您在编译时不知道任何一边的动态类型(例如静态类型与动态类型相同),并且实际上您想通过指针或引用来比较两个对象,那么你将不得不进行两个虚拟呼叫(双派)或使用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

这个特殊的解决方案要求'Expr'事先知道所有派生类型,比如'NumExpr' ......需要在代码中有一个“前向声明”......如果不更改它,就不能添加新的表达式母亲'Expr'的代码... – user1055604

+0

这当然避免了'dynamic_cast',但是当有很多子类时变得很难处理,并且首先破坏了形成类层次结构的原因。 – wcochran

0

没有可以用来代替dynamic_cast初始类型匹配的typeid Operator

(src)

+0

真的只是一样的方法。 – wcochran

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; 
    } 
    } 
}; 
+0

'e'是什么?不编译, – wcochran

+0

static_cast的结果,更正 – Sammy

+0

使用'typeid'和'static_cast'与使用'dynamic_cast'没有什么不同。 – wcochran