我有一个表示数学表达式的树,我想为了计算表达式树的值,我会实现访问者模式,但是在C ++中这涉及到很多重复自己,作为方法接受访问者必须在每个子类上,因为即使方法相同,类型也不是。
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 */ }
};
这也有一个问题,因为这些方法是虚拟的,你不能拥有模板节点。
拥有枚举似乎要容易得多,每个节点都有一个案例,你只需在方法而不是访问者模式中打开枚举。
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:
//...
}
}
我知道枚举版本需要动态转换,但总体而言似乎更需要重复大量相同的代码,这意味着它将允许模板节点,例如(BinOpNode<std::plus>
)。
或者有一些方法可以改善C ++中的访问者模式,所以它不需要所有这些重复吗?
(直接在stackoverflow中输入代码,对不起任何错误,但它基于实际代码)
编辑:对于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));
答案 0 :(得分:2)
您可以使用模板删除重复:
// 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 */ }
};
请注意,您的模板节点代码将不正常工作,因为以下行无效:
BinOpNode* n = dynamic_cast<BinOpNode*>(node);
BinOpNode
您定义为模板,并且您不能指向模板,只能指向类的指针。例如,您可能有指向BinOpNode
生成的特定类的指针,例如
BinOpNode<int>* n = dynamic_cast<BinOpNode<int>*>(node);
但这也完全由访客模式处理;使用上面的模板代码:
class Visitor:
public visits<BinOpNode<int> >,
public visits<BinOpNode<double> >,
...
{
};
template<typename T> class BinOpNode:
public ConcreteNode<BinOpNode<T> >
{
...
};