如何避免具有不同类型节点的树形结构中的代码重复

时间:2018-10-18 10:40:48

标签: c++

我具有以下层次结构(仅作为示例):

// nodes hierarchy:
// NodeA - NodeB1 - NodeC1 - NodeD1
//                         - NodeD2
//       - NodeB2 - NodeC2
//       - NodeB3 - NodeC3
//                - NodeC4

所有节点都从相同的基类继承。层次结构是固定的,每个节点都有来自业务逻辑的独特功能。我想获得具有确切类型的子节点/父节点(即当我具有其父节点的类型时,使用NodeC2 *代替Node *:NodeB2 *代替Node *)。工作实现如下所示。问题是在具体的吸气剂的实现中如何避免大型代码重复

// Node.h
#include <vector>
struct Node {
    Node* getParent() { return parent; }
    Node* getChild(size_t i) { return children[i]; }
    virtual ~Node() {}

    Node* parent;
    std::vector<Node*> children;
};

// NodeB3.h

// forward declarations
struct NodeA;
struct NodeC3;
struct NodeC4;

struct NodeB3 : public Node {
// awful code duplication inside each NodeX.h
    NodeA* getNodeA();
    NodeC3* getNodeC3();
    NodeC4* getNodeC4();
};

// NodeA.h
class NodeB1;
class NodeB2;
class NodeB3;
struct NodeA  : public Node {
    NodeB1* getNodeB1();
    NodeB2* getNodeB2();
    NodeB3* getNodeB3();
};

// NodeC3.h
class NodeB3;
struct NodeC3 : public Node {
    NodeB3* getNodeB3();
};
// NodeC4.h
class NodeB3;
struct NodeC4 : public Node {
    NodeB3* getNodeB3();
};

// NodeB3.cpp

#include <cassert>

// awful code duplication inside each NodeX.cpp
NodeA* NodeB3::getNodeA() {
    return static_cast<NodeA*>(getParent()); 
}
NodeC3* NodeB3::getNodeC3() {
    return static_cast<NodeC3*>(getChild(0)); 
}
NodeC4* NodeB3::getNodeC4() {
    return static_cast<NodeC4*>(getChild(1)); 
}

// main.cpp
int main() {
    NodeB3* nb3 = new NodeB3();
    nb3->parent = new NodeA();
    nb3->children.push_back(new NodeC3());
    nb3->children.push_back(new NodeC4());

    // desired use
    NodeA* na = nb3->getNodeA();
    NodeC3* nc3 = nb3->getNodeC3();
    NodeC4* nc4 = nb3->getNodeC4();
}

1 个答案:

答案 0 :(得分:1)

您没有有意义的基类,因此请勿尝试使用。

编写访问者所需的各种遍历。

struct NodeB1;
struct NodeB2;
struct NodeB3;  
struct NodeA {
    std::unique_ptr<NodeB1> child1;
    std::unique_ptr<NodeB2> child2;
    std::unique_ptr<NodeB3> child3;

    template<typename UnaryOperation>
    void walk_down_tree(UnaryOperation unary_op)
    {
        unary_op(*this);
        child1->walk_down_tree(unary_op);
        child2->walk_down_tree(unary_op);
        child3->walk_down_tree(unary_op);
    }

    template<typename UnaryOperation>
    void walk_up_tree(UnaryOperation unary_op)
    {
        unary_op(*this);
    }
};

struct NodeC1;
struct NodeB1 {
    NodeA * parent;
    std::unique_ptr<NodeC1> child1;

    template<typename UnaryOperation>
    void walk_down_tree(UnaryOperation unary_op)
    {
        unary_op(*this);
        child1->walk_down_tree(unary_op);
    }

    template<typename UnaryOperation>
    void walk_up_tree(UnaryOperation unary_op)
    {
        unary_op(*this);
        parent->walk_up_tree(unary_op);
    }
};

// ... and so on

并采取类似行动

struct PrintName { 
    template<typename Node>
    void operator()(Node & node)
    {
        std::cout << node.name << std::endl;
    }
};

struct Frobnicate {
    Foo results;
    void operator()(NodeA & a)
    {
         results.frobA(a);
    }
    void operator()(NodeB1 & b1)
    {
         results.frobB(b1);
    }
    // more overloads ...
};

有关类似方案,另请参见std::visit