空对象模式,递归类和前向声明

时间:2009-11-30 20:16:16

标签: c++ recursion object null default

我有兴趣做类似以下的事情来遵守Null对象设计模式并避免多产的NULL测试:

class Node;
Node* NullNode;

class Node {
public:
  Node(Node *l=NullNode, Node *r=NullNode) : left(l), right(r) {};
private:
  Node *left, *right;
};

NullNode = new Node();

当然,正如所写,NullNode在Node类声明之前和之后具有不同的内存位置。如果您不想使用默认参数(例如,删除Node * r = NullNode),则可以在没有前向声明的情况下执行此操作。

另一种选择是使用一些继承:使用两个子节点(NullNode和FullNode)创建父类(Node)。然后上面的节点示例将是FullNode的代码,上面代码中的NullNode将是继承自Node的NullNode类型。我讨厌通过诉诸继承来解决简单的问题。

所以,问题是:如何在C ++中使用默认参数(同一类的实例!)将Null对象模式应用于递归数据结构(类)?

3 个答案:

答案 0 :(得分:3)

使用extern

extern Node* NullNode;
...
Node* NullNode = new Node();

更好的是,让它成为一个静态成员:

class Node {
public:
  static Node* Null;
  Node(Node *l=Null, Node *r=Null) : left(l), right(r) {};
private:
  Node *left, *right;
};

Node* Node::Null = new Node();

也就是说,在现有代码和上述修正中,您泄漏了Node的实例。你可以使用auto_ptr,但这是危险的,因为全局变量和静力学的破坏顺序不确定(一些全局的析构函数可能需要Node::Null,它可能已经或者可能已经消失了) 。

答案 1 :(得分:3)

我实际上已经实现了一个递归树(对于JSON等)做这样的事情。基本上,您的基类成为“NULL”实现,其接口是派生的所有接口的并集。然后,您可以使用派生类来实现这些部分 - “DataNode”实现数据获取器和设置器等。

这样,您可以编程到基类接口并为自己节省很多痛苦。您可以设置基本实现来为您执行所有样板逻辑,例如

class Node {
    public:
    Node() {}
    virtual ~Node() {}

    virtual string OutputAsINI() const { return ""; }
};

class DataNode {
    private:
    string myName;
    string myData;

    public:
    DataNode(const string& name, const string& val);
    ~DataNode() {}

    string OutputAsINI() const { string out = myName + " = " + myData; return out; }
};

这样我就不必测试任何东西 - 我只是盲目地称之为“OutputAsINI()”。整个界面的类似逻辑将使大多数空测试消失。

答案 2 :(得分:0)

反转层次结构。将空节点放在基数:

class Node {
public:
  Node() {}
  virtual void visit() const {}
};

然后根据需要专门化:

template<typename T>
class DataNode : public Node {
public:
  DataNode(T x, const Node* l=&Null, const Node* r=&Null)
    : left(l), right(r), data(x) {}

  virtual void visit() const {
    left->visit();
    std::cout << data << std::endl;
    right->visit();
  }

private:
  const Node *left, *right;
  T data;
  static const Node Null;
};

template<typename T>
const Node DataNode<T>::Null = Node();

样本用法:

int main()
{
  DataNode<char> a('A', new DataNode<char>('B'),
                        new DataNode<char>('C'));

  a.visit();

  return 0;
}

输出:

$ ./node 
B
A
C