简体   繁体   中英

Binary search tree recursive destructor

I have checked other similar topics, but none seemed to help me.

I am trying to write a destructor for this specific BST implementation. Every node contains a pointer to the parent, a pointer to the left node, a pointer to the right node and a pointer to the value it contains. This is how the beginning of the class looks:

#ifndef BST_H
#define BST_H

#include <iostream>

template <typename T>
class BST{
    private:
        BST<T> *left;
        BST<T> *right;
        BST<T> *parent;
        T *value;

    public:
        BST() {
            this->parent = NULL;
            this->left = NULL;
            this->right = NULL;
            this->value = NULL;
        }

        ~BST() {
            removeRecursively(this);
        }

        void removeRecursively(BST<T>* node) {
            if (node->left != NULL)
                removeRecursively(node->left);
            if (node->right != NULL)
                removeRecursively(node->right);
            if (node->left == NULL && node->right == NULL) {
                if (node->parent->left == node)
                    node->parent->left = NULL;
                if (node->parent->right == node)
                    node->parent->right = NULL;

                node->parent = NULL;
                node->value = NULL;
                delete node->value;
                delete node;
            }
        }

        void add(T value) {
            if (this->value == NULL) { // root-only case
                this->value = new T;
                *(this->value) = value;
            }
            else {
                if (value < *(this->value)) {
                    if (this->left != NULL) // has left child
                        this->left->add(value);
                    else { // has no left child
                        this->left = new BST<T>;
                        this->left->value = new T;
                        this->left->parent = this;
                        *(this->left->value) = value;
                    }
                }
                else {
                    if (this->right != NULL) // has right child
                        this->right->add(value);
                    else { // has no right child
                        this->right = new BST<T>;
                        this->right->value = new T;
                        this->right->parent = this;
                        *(this->right->value) = value;
                    }
                }
            }
        }

        void inOrderDisplay() {
            if (this->left != NULL)
                this->left->inOrderDisplay();
            std::cout << *(this->value) << " ";
            if (this->right != NULL)
                this->right->inOrderDisplay();
        }

        BST<T>* search(T value) {
            if (*(this->value) == value)
                return this;
            else if (this->left != NULL && value < *(this->value))
                return this->left->search(value);
            else if (this->right != NULL && value > *(this->value))
                return this->right->search(value);
            else
                return NULL;
        }

        BST<T>* remove(T value) {
            BST<T>* node = search(value);

            if (node != NULL) {
                if (node->left == NULL && node->right == NULL) { // leaf node
                    delete node->value;
                    if (node->parent->left == node) // is left child
                        node->parent->left = NULL;
                    else // is right child
                        node->parent->right = NULL;
                    delete node;
                }

                // 1 child nodes
                if (node->left != NULL && node->right == NULL) { // has left child
                    if (node->parent->left == node) // is left child
                        node->parent->left = node->left;
                    else // is right child
                        node->parent->right = node->left;
                    delete node->value;
                    node->parent = NULL;
                    delete node;
                }

                if (node->left == NULL && node->right != NULL) { // has right child
                    if (node->parent->left == node) // is left child
                        node->parent->left = node->right;
                    else // is right child
                        node->parent->right = node->right;
                    delete node->value;
                    node->parent = NULL;
                    delete node;
                }

                // 2 children nodes
                if (node->left != NULL && node->right != NULL) {
                    T aux;
                    BST<T>* auxNode = node->right;

                    while (auxNode->left != NULL)
                    auxNode = auxNode->left;

                    aux = *(auxNode->value);

                    if (auxNode->right != NULL) {
                        *(auxNode->value) = *(auxNode->right->value);
                        auxNode->right->parent = NULL;
                        auxNode->right->value = NULL;
                        auxNode->right = NULL;
                        delete auxNode->right;
                    }
                    else {
                        if (auxNode->parent->left == auxNode) // is left child
                            auxNode->parent->left = NULL;
                        else // is right child
                            auxNode->parent->right = NULL;
                        auxNode->value = NULL;
                        delete auxNode;
                    }
                    *(node->value) = aux; 
                }
            }

            return this;
        }
};
#endif // BST_H

The BST class is used as below:

BST<int>* root = new BST<int>();

root->add(5);
root->add(2);
root->add(-17);

root->inOrderDisplay();

root->remove(5);

I mention that all the methods work properly (I decided not to post them, since they are not the subject of this question). The problem is that when I run my test file with Valgrind, it detects some memory leaks and I am sure that they occur because of the lack of a proper destructor (the above one produces a segmentation fault).

EDIT: I added the code for the other methods

Thank you!

Your basic design is kind of broken, at least IMO. That is, if you're willing to jump through enough hoops, you can probably make it work, but even at best it'll probably always be at least a little clumsy to work with.

I'd start by defining a separate class for the nodes in the tree. Then the tree itself will hold a pointer to the root of the tree and define most of the interface to the tree.

template <class T>
class Tree {

    struct node {
        node *parent;
        node *left;
        node *right;
        T *value;

        node(T *value) 
            : parent(nullptr), left(nullptr), right(nullptr), value(new T(value))
        {             
        }

        ~node() { 
            delete(left);
            delete(right);
            delete(value);
        }           
    } *root;

    Tree() : root(nullptr) {}

    ~Tree() { 
        delete(root);
    }
};

Destruction doesn't have to be explicitly recursive. The delete(left) (for example) will invoke the dtor for the left child node (if there is one) and that'll invoke the dtor for its child nodes, and so on. When we reach a leaf node, we'll end up with (the equivalent of) delete nullptr; , which is defined as doing nothing, stopping the recursion.

You should swap the lines:

            node->value = NULL;
            delete node->value;

Like this:

            delete node->value;
            node->value = NULL;

if you first assign NULL you delete nothing.

The hardest bugs to find are the ones in the code you've decided not to look at because you've convinced yourself they're not relevant.

Anyways, there is one evident problem with your implementation: after removing a bunch of other stuff, you are basically doing

class foo
{
    ~foo() { delete this; }
};

Use RAII with unique_ptr to avoid those issues:

template <typename T>
class BST{
public:
    BST() = default;
    ~BST() = default;

    BST(const BST&) = delete;
    BST& operator =(const BST&) = delete;
    BST(BST&&) = delete;
    BST& operator =(BST&&) = delete;

private:
    std::unique_ptr<BST<T>> left;
    std::unique_ptr<BST<T>> right;
    BST<T>* parent = nullptr;
    std::unique_ptr<T> value;
};

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM