Pregunta

Estoy interesado en hacer algo como lo siguiente para adherirme a un patrón de diseño de Objetos Nulos y evitar prolíficas pruebas 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();

Por supuesto, como está escrito, NullNode tiene diferentes ubicaciones de memoria antes y después de la declaración de la clase Node. Puede hacerlo sin la declaración de reenvío, si no desea tener argumentos predeterminados (es decir, eliminar Node * r = NullNode).

Otra opción usaría algo de herencia: crear una clase padre (Nodo) con dos hijos (NullNode y FullNode). Entonces, el ejemplo de nodo anterior sería el código para FullNode y el NullNode en el código anterior sería del tipo NullNode heredado de Node. Odio resolver problemas simples apelando a la herencia.

Entonces, la pregunta es: ¿cómo aplicar patrones de objetos nulos a estructuras de datos recursivas (clases) con argumentos predeterminados (que son instancias de esa misma clase) en C ++?

¿Fue útil?

Solución

Use extern :

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

Mejor aún, conviértalo en un miembro estático:

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();

Dicho esto, tanto en el código existente como en las enmiendas anteriores, se pierde una instancia de Node . Podría usar auto_ptr , pero eso sería peligroso debido al orden incierto de destrucción de las variables globales y estáticas (un destructor de algunos global puede necesitar Node :: Null , y puede o puede que ya no se haya ido para entonces).

Otros consejos

He implementado un árbol recursivo (para JSON, etc.) haciendo algo como esto. Básicamente, su clase base se convierte en " NULL " implementación, y su interfaz es la unión de todas las interfaces para el derivado. Luego tiene clases derivadas que implementan las piezas- '' DataNode '' implementa captadores y establecedores de datos, etc.

De esa manera, puede programar en la interfaz de la clase base y ahorrarse MUCHO dolor. Configura la implementación base para hacer toda la lógica repetitiva por usted, por ejemplo,

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

De esta manera no tengo que probar nada, simplemente llamo ciegamente " OutputAsINI () " ;. Una lógica similar para toda su interfaz hará que la mayoría de las pruebas nulas desaparezcan.

Invierte la jerarquía. Coloque el nodo nulo en la base:

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

Luego, especialízate según sea necesario:

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();

Ejemplo de uso:

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

  a.visit();

  return 0;
}

Salida:

$ ./node 
B
A
C
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top