2016-08-03 2 views
2

J'ai un arbre qui représente une expression mathématique, et j'ai pensé que pour calculer la valeur d'un arbre d'expression, j'implémenterais le modèle de visiteur, mais en C++ ceci implique un Beaucoup de choses à se répéter, comme les méthodes pour accepter un visiteur doivent être sur chaque sous-classe, car même si les méthodes sont identiques, les types ne sont pas.Utiliser enums et passer au lieu du modèle de visiteur

class Node { 
    virtual void Acccept(Visitor *visitor) = 0; 
}; 
class ConstantNode : Node { 
    virtual void Accept(Visitor *visitor) { 
    visitor->visit(this); 
    } 
}; 

class VariableNode : Node { 
    virtual void Accept(Visitor *visitor) { 
    visitor->visit(this); 
    } 
}; 

class Visitor { 
    virtual void visit(ConstantNode *node) = 0; 
    virtual void visit(VariableNode *node) = 0; 
}; 
class CalculateVisitor : Visitor { 
    virtual void visit(ConstantNode *node) { /* code */ } 
    virtual void visit(VariableNode *node) { /* code */ } 
}; 

Cela a aussi le problème parce que les méthodes sont virtuelles, vous ne pouvez pas avoir des nœuds de modèles.

Il semble simplement beaucoup plus facile d'avoir une énumération, où il y a un cas pour chaque nœud et il suffit d'activer l'énumération dans une méthode au lieu du modèle de visiteur.

class Node { 
    enum NodeType { 
    Constant, 
    Variable 
    } 
    Node(NodeType type) : m_nodeType(type) {} 
    NodeType m_nodeType; 
}; 

class ConstantNode { 
    ConstantNode() : Node(Constant) {} 
}; 
class VariableNode { 
    VariableNode() : Node(Variable) {} 
}; 

int calculate(Node* node) { 
    switch (node->m_nodeType) { 
    case Constant: 
     //... 
    case Variable: 
     //... 
    } 
} 

Je sais la version ENUM nécessiterait la coulée dynamique, mais dans l'ensemble qui semble préférable d'avoir à répéter beaucoup du même code, et cela signifie qu'il permettrait des modèles noeuds par exemple (BinOpNode<std::plus>).

Vous pouvez également améliorer le modèle de visiteur en C++, de sorte qu'il n'a pas besoin de toute cette répétition?

(dactylographier le code directement dans stackoverflow, désolé pour les erreurs, mais il est basé sur le code réel)

EDIT: pour BinOpNode:

template<class T> 
class BinOpNode { 
    T m_op = T(); 
    BinOpNode() : Node(BinOp) {} 
}; 

//in calculate: 
case BinOpNode: 
    BinOpNode* n = dynamic_cast<BinOpNode*>(node); 
    return n->m_op(calculate(n->m_left), calculate(n->m_right)); 
+1

Cela ne semble pas plus facile du tout. La quantité de répétition est la même (étiquettes de cas vs fonctions virtuelles). Vous n'avez pas non plus expliqué comment vous prévoyez de traiter les modèles. 'Cas BinOp:' maintenant quoi? –

+0

Il y a moins de répétitions car cela supprime le besoin de toutes les fonctions Accept. Il y aura des constructeurs pour les nœuds dans les deux cas. –

+0

Ajout d'un nouveau visiteur ne nécessite pas de répétition. et évitez d'oublier un type contraire au cas switch/enum. – Jarod42

Répondre

1

Vous pouvez supprimer la répétition en utilisant des modèles:

// Classes implementing the mechanism 

class Node { 
    virtual void Accept(Visitor *visitor) = 0; 
}; 

template<typename NodeType> class ConcreteNode 
{ 
    virtual void Accept(Visitor* visitor) 
    { 
    visits<NodeType>* v = visitor; 
    v->visit((NodeType*)this); 
    } 
}; 

template<typename NodeType> class visits 
{ 
    virtual void visit(NodeType* node) = 0; 
}; 

// The actual visitors/visited classes 

class Visitor: 
    visits<ConstantNode>, 
    visits<VariableNode> 
{ 
}; 

class ConstantNode : ConcreteNode<ConstantNote> {}; 
class VariableNode : ConcreteNode<VariableNode> {}; 

class CalculateVisitor : Visitor { 
    virtual void visit(ConstantNode *node) { /* code */ } 
    virtual void visit(VariableNode *node) { /* code */ } 
}; 

Notez que votre code de nœud de modèle ne pas travail, la ligne suivante ne peut pas fonctionner:

BinOpNode* n = dynamic_cast<BinOpNode*>(node); 

BinOpNode vous définir comme un modèle, et vous ne pouvez pas avoir un pointeur vers un modèle, seul un pointeur vers une classe. Vous pourriez, par exemple, un pointeur vers une spécifique classe générée à partir BinOpNode, comme

BinOpNode<int>* n = dynamic_cast<BinOpNode<int>*>(node); 

mais qui est parfaitement géré par le motif des visiteurs ainsi; avec le code de modèle ci-dessus:

class Visitor: 
    public visits<BinOpNode<int> >, 
    public visits<BinOpNode<double> >, 
    ... 
{ 
}; 

template<typename T> class BinOpNode: 
    public ConcreteNode<BinOpNode<T> > 
{ 
    ... 
};