我有一个表示数学表达式的树,我想为了计算表达式树的值,我会实现访问者模式,但是在 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:
      //...
  }
}

我知道 enum 版本需要动态转换,但总的来说,似乎最好重复很多相同的代码,这意味着它允许模板节点,例如( 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));

最佳答案

您可以使用模板删除重复:

// 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> >
{
  ...
};

关于c++ - 使用枚举和开关代替访问者模式,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/38740051/

10-11 17:18