2016-08-03 17 views
2

Ich habe einen Baum, der einen mathematischen Ausdruck darstellt, und ich dachte, um den Wert eines Ausdrucksbaums zu berechnen, würde ich das Besuchermuster implementieren, aber in C++ beinhaltet dies Ich wiederhole mich selbst, da die Methoden, einen Besucher zu akzeptieren, in jeder Unterklasse sein müssen, denn obwohl die Methoden identisch sind, sind es die Typen nicht.Verwenden von Enums und wechseln statt Besuchermuster

Dies hat auch das Problem, dass, weil die Methoden virtuell sind, Sie keine Vorlagenknoten haben können.

Es scheint einfach viel einfacher zu sein, eine enum zu haben, wo es einen Fall für jeden Node gibt und du einfach die enum in einer Methode anstelle des Besuchermusters anschaltest.

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: 
     //... 
    } 
} 

Ich weiß, die ENUM-Version dynamische Gießen erfordern würde, aber alles in allem, das scheint zu bevorzugen viele den gleichen Code zu wiederholen zu haben, und es bedeutet, dass es für Vorlagen Knoten zB (BinOpNode<std::plus>) ermöglichen würde.

Alternativ gibt es eine Möglichkeit, das Besuchermuster in C++ zu verbessern, so dass es nicht alle diese Wiederholung benötigt?

(der Code direkt in Stackoverflow typisierten, sorry für Fehler, aber es ist auf echten Code basiert)

EDIT: für 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

Es scheint überhaupt nicht einfacher. Die Anzahl der Wiederholungen ist die gleiche (Case Labels vs virtuelle Funktionen). Sie haben auch nicht erläutert, wie Sie Vorlagen verarbeiten möchten. 'Fall BinOp:' Was nun? –

+0

Es gibt weniger Wiederholungen, da alle Accept-Funktionen entfallen. Es wird Konstruktoren für die Knoten in jeder Situation geben. –

+0

Das Hinzufügen eines neuen Besuchers erfordert keine Wiederholung. und vermeiden Sie, eine Art zu vergessen, die dem Schalter/enum Fall entgegengesetzt ist. – Jarod42

Antwort

1

Sie die Wiederholung unter Verwendung von Vorlagen entfernen:

// 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 */ } 
}; 

Beachten Sie, dass Ihr Vorlagenknotencode nicht funktioniert, da die folgende Zeile nicht funktionieren kann:

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

BinOpNode definieren Sie als Vorlage, und Sie können nicht einen Zeiger auf eine Vorlage haben, nur einen Zeiger auf eine Klasse. Sie könnten zum Beispiel einen Zeiger auf eine Klasse spezifischen erzeugt aus BinOpNode, wie

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

aber das perfekt durch das Besuchermuster als auch behandelt; mit dem Vorlagencode oben:

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

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