Search code examples
c++data-structurestreepointer-to-member

Problem with general tree implementation in C++


I have to implement a general tree en C++ for one of my class, and I come across a problem I don't understand.

I have two classes, EmployeeNode and EmpoyeeTree. EmployeeNode contains the data elements needed for the work : a string name, an EmployeeNode parent and a List<EmployeeNode> children which is a linked list I implemented before, supposedly working with any template object.

Here is my code for the moment:

class EmployeeTree;

class EmployeeNode {
public:
    EmployeeNode(std::string name, EmployeeNode* parent, List<EmployeeNode>* child);
    ~EmployeeNode();
    
    void setChild(EmployeeNode newEmployee) {child->insert(newEmployee);}
    List<EmployeeNode>* getChild() {return(child);}
    bool hasChild() {return (child != 0);}
    
    std::string getName() {return name;}
    
private:
    std::string name;
    EmployeeNode *parent;
    List<EmployeeNode> *child;
};

EmployeeNode::EmployeeNode(std::string employeeName, EmployeeNode* employeeParent, List<EmployeeNode>* employeeChildren)
:name(employeeName), parent(employeeParent), child(employeeChildren)
{
    employeeChildren = new List<EmployeeNode>;
}

EmployeeNode::~EmployeeNode() {}

class EmployeeTree {
public:
    EmployeeTree();
    ~EmployeeTree();
    
    void hireEmployee(EmployeeNode *newEmployee);
    void hireEmployee(EmployeeNode* boss, std::string newEmployee);
    
    EmployeeNode find(std::string employee);
    
    void print(EmployeeTree Tree);
    
private:
    int level, age;
    EmployeeNode *root;
};

EmployeeTree::EmployeeTree()
:root(0)
{}

EmployeeTree::~EmployeeTree()
{}

void EmployeeTree::hireEmployee(EmployeeNode *newEmployee)
{
    root = newEmployee;
}

void EmployeeTree::hireEmployee(EmployeeNode* boss, std::string newEmployee)
{
    EmployeeNode* newChild;
    
    if (!boss->hasChild()){
        newChild = new EmployeeNode(newEmployee, boss, 0);
        boss->setChild(*newChild);
    }
    
    else {
        newChild = new EmployeeNode(newEmployee, boss, boss->getChild());
        boss->setChild(*newChild);
    }
}

EmployeeNode EmployeeTree::find(std::string employee) {
    if(root->getName() == employee)
        return *root;
    
    else if (root->getChild()) {
        
        List<EmployeeNode> *children = root->getChild();
        children->gotoBeginning();
        
        for(children->getCursor(); children->getCursor().getName() == employee ;children->gotoNext())
            *root = children->getCursor();
        
        return(*root);
        }
    else {std::cout << "Boss not found in employee tree." << std::endl;}
    
    return(*root);
}

For now, I'm just trying some basic commands to test my work. I first create the root EmployeeNode with hireEmployee(EmployeeNode *newEmployee), and then I try to add a child to that with hireEmployee(EmployeeNode *boss, std::string newEmployee), but I get an error telling me I try to add the child in a non-existing children list. I checked, but I don't understand where or what my error is.

When debugging with breakpoints, I found that every time it is created, the List<EmployeeNode> is automatically destructed after. I think I played too much with pointers without fully understanding them, but now I stuck with this.


Solution

  • There are some structural problems in EmployeeNode.

    1. List<EmployeeNode> *child; shouldn't it be List<EmployeeNode *> child; which is to represent that every EmployeeNode have a member called child to remember a list of pointer to its child?
    2. In the constructor
    :name(employeeName), parent(employeeParent), child(employeeChildren)
    {
        employeeChildren = new List<EmployeeNode>;
    }
    

    child will be first initialized by employeeChildren in the argument, and then employeeChildren will be set to a new list and have no effect on child Also, why does the constructor need to import other people's child?


    Just to be complete, I also provide my implementation for your reference. Don't be overwhelmed if I use anything you haven't learned yet.

    #include <iostream>
    #include <list>
    #include <memory>
    
    template<typename T>
    using List = std::list<T>;
    class EmployeeNode;
    using EmployeeNodePtr = std::unique_ptr<EmployeeNode>;
    
    class EmployeeNode
    {
    public:
        EmployeeNode(std::string name, EmployeeNode* parent): name{name}, parent{parent} {}
        void setChild(EmployeeNodePtr &child) { children.push_back(std::move(child)); }
    
        auto findChildByName(std::string queryname) -> EmployeeNode*
        {
            for (EmployeeNodePtr& child : children)
                if (child->name == queryname)
                    return child.get();
    
            for (EmployeeNodePtr& child : children)
            {
                EmployeeNode* n = child->findChildByName(queryname);
                if (n != nullptr)
                    return n;
            }
    
            return nullptr;
        }
        auto getName() -> std::string { return name; }
        void print()
        {
            std::cout << name << "\n";
            for (EmployeeNodePtr& child : children)
                child->print();
        }
    
    private:
        std::string name;
        EmployeeNode *parent; // reference to parent, no ownership
        List<EmployeeNodePtr> children;
    };
    
    class EmployeeTree
    {
    public:
        void changeCEO(EmployeeNodePtr newCEO) { root.swap(newCEO); }
        void hireEmployee(EmployeeNode* boss, std::string newEmployee)
        {
            EmployeeNodePtr newChild = std::make_unique<EmployeeNode>(newEmployee, boss);
            boss->setChild(newChild);
        }
    
        auto find(std::string employee) -> EmployeeNode*
        {
            if (root->getName() == employee)
                return root.get();
            return root->findChildByName(employee);
        }
    
        void print() { root->print(); }
    private:
        EmployeeNodePtr root;
    };
    
    int main()
    {
        EmployeeNodePtr ceo = std::make_unique<EmployeeNode>("GreatCEO", nullptr);
        EmployeeTree company;
        company.changeCEO(std::move(ceo));
    
        EmployeeNode* boss = company.find("GreatCEO");
        company.hireEmployee(boss, "RightHand");
        company.hireEmployee(boss, "LeftHand");
        company.hireEmployee(boss, "RightFoot");
        company.hireEmployee(boss, "LeftFoot");
    
        EmployeeNode* hand = company.find("RightHand");
        company.hireEmployee(hand, "Finger1");
    
        EmployeeNode* feet = company.find("LeftFoot");
        company.hireEmployee(feet, "Toe");
    
        company.print();
    }