2012-12-28 3 views
4

표현식에 대한 구문 트리를 비교할 수 있기를 원합니다. 기본 클래스 Expr 구체적인 서브 클래스가 오버라이드 (override) 할 수있는 순수 가상 compare 방법이있다 : 예를 들어C++ 동적 객체 클래스 비교

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

NumExpr 말과 AddExpr는 리터럴 정수 표현을 나타내는 두 개의 구상 서브 클래스이며, 이진은 각각 표현을 추가합니다. 더 적절한 거기 - 나는 dynamic_cast를 사용할 때

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); 
    } 
}; 

난 항상 내가 뭔가 잘못하고있는 중이 야 기분 각 compare 방법을 수행하는 첫 번째 일은 확인 other 표현이 동일한 유형인지 확인하기 위해 dynamic_cast를 사용하다 접근 방식을 통해 dynamic_cast을 사용하지 않고도 객체 간의 동적 비교를 수행 할 수 있습니까?

visitor design pattern을 사용하면 이 아닌은 (내가 알 수있는 한) RTTI의 필요성을 해결합니다. 에 "표현의 방문자"에 대한 추상 기본 클래스는 다음과 같이 보일 수 있습니다

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

구체적인 표현 서브 클래스가 두 번 를 사용

class NumExpr; 
class AddExpr; 

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

표현의 기본 클래스는 순수 가상 accept 방법을 포함

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); 
    }; 
}; 

: 적절한 visit 메소드를 호출을 파견 마침내이 메커니즘을 사용하여 표현식 비교를 수행 할 때 여전히이 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) {} 
    ... 
}; 

그리고 우리는 RTTI를 방지하기 위해 static_castflavor 필드를 사용할 수 있습니다

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

'기본 표현식이 같은 경우'compare 함수가 true를 반환하고 다른 모든 경우에 false를 반환하는 것이 맞습니까? – user1055604

답변

1

컴파일 타임에 양쪽의 동적 유형을 모르는 경우 (예 : 정적 유형이 동적 유형과 같음) 실제로 두 개의 Expr 오브젝트를 포인터 또는 참조로 비교하려는 경우, 두 번 가상 전화를 걸거나 (이중 발송) dynamic_cast을 사용해야합니다.

그것은 다음과 같이 보일 것입니다 : 초기 유형 매칭 대신 dynamic_cast으로 사용할 수있는 typeid Operator있다

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'과 같은 모든 파생 된 타입을 알고 있어야합니다. 코드에서'forward declaration'을 요구할 것입니다. 코드를 변경하지 않고 새로운 표현식을 추가 할 수 없습니다. mother'Expr'을위한 코드 ... – user1055604

+0

이것은 확실히'dynamic_cast'를 피하지만, 수많은 서브 클래스가있을 때 다루기 힘들어지며 처음에는 클래스 계층을 형성하는 이유를 뒤집습니다. – 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