c++ - 通用二叉树节点析构函数问题

标签 c++ data-structures binary-tree destructor

我一直在做一项作业,现在我被有问题的析构函数困住了。我必须创建一个具有所有常用成员函数和一些特殊运算符的通用二叉树。还有一个限制:一切都必须迭代工作,所以这次没有讨厌的递归黑客。

BinTreeNode 类的析构函数显然有问题,因为如果我像这样删除节点:

BinTreeNode<int> * node = new BinTreeNode<int>();
delete node; 

我仍然可以访问它的数据:

node->getData(); //should fail miserably

所以删除没有效果,但我不知道应该如何更正析构函数。 在我看来,该算法应该是正确的,所以我怀疑我使用指针的方式有问题,但在这一点上我很困惑,我什至不理解我自己的代码。

到目前为止我的代码:

二叉树.h

#ifndef BINTREE_H_
#define BINTREE_H_

#ifndef NULL
#define NULL 0
#endif

#include "BinTreeNode.h"

template <class T>
class BinTree
{
    private:
        BinTreeNode<T> * root;
    public:
        //constructors and destructor
        BinTree():
            root(NULL){}

        BinTree(T data):
            root(new BinTreeNode<T>(data)){}

        ~BinTree();

        //search
        BinTreeNode<T> * search(T data);

        //insert
        bool insert(T data);

        //remove
        bool remove(T data);
};

template <class T>
BinTree<T>::~BinTree()
{
    delete root;
}

template <class T>
BinTreeNode<T> * BinTree<T>::search(T data)
{
    BinTreeNode<T> * node = new BinTreeNode<T>(data);
    BinTreeNode<T> * current = root;

    while (current != NULL)
    {
        if (*current == *node)
        {
            delete node;
            return root;
        }
        else if (*node < *current)
        {
            current = current->getLeft();
        }
        else
        {
            current = current->getRight();
        }
    }
    delete node;
    return NULL;
}

template <class T>
bool BinTree<T>::insert(T data)
{
    BinTreeNode<T> * node = new BinTreeNode<T>(data);
    BinTreeNode<T> * current = root;

    while (current != NULL)
    {
        if (*current == *node)
        {
            delete node;
            return false;
        }
        else if (*node < *current)
        {
            if (current->getLeft() == NULL)
            {
                current->setLeft(node);
                return true;
            }
            else
            {
                current = current->getLeft();
            }
        }
        else
        {
            if (current->getRight() == NULL)
            {
                current->setRight(node);
                return true;
            }
            else
            {
                current = current->getRight();
            }
        }
    }
    return false;
}

#endif

BinTreeNode.h

#ifndef BINTREENODE_H_
#define BINTREENODE_H_

#ifndef NULL
#define NULL 0
#endif

template <class T>
class BinTreeNode
{
    private:
        T data;
        BinTreeNode<T> *left, *right, *parent;
    public:
        //constructors and destructor
        BinTreeNode():
            data(NULL), left(NULL), right(NULL), parent(NULL){}

        BinTreeNode(T data):
            data(data), left(NULL), right(NULL), parent(NULL){}

        ~BinTreeNode();

        //set and get data member
        T getData() const;

        void setData(T data);

        //set and get left and right branches
        BinTreeNode<T> * getLeft() const;

        BinTreeNode<T> * getRight() const;

        void setLeft(BinTreeNode<T> * node);

        void setRight(BinTreeNode<T> * node);

        //set and get parent
        BinTreeNode<T> * getParent() const;

        void setParent(BinTreeNode<T> * node);

        //comparison operators
        bool operator<(const BinTreeNode<T>& node) const;
        bool operator>(const BinTreeNode<T>& node) const;
        bool operator==(const BinTreeNode<T>& node) const;
};

template <class T>
BinTreeNode<T>::~BinTreeNode()
{
    BinTreeNode<T> * current = this;
    BinTreeNode<T> * parent = NULL;
    while (current != NULL)
    {
        parent = current->getParent();
        if (current->getLeft() == NULL)
            current = current->getLeft();
        else if (current->getRight() == NULL)
            current = current->getRight();
        else
        {
            if (parent->getRight() == current)
                parent->setRight(NULL);
            else
                parent->setLeft(NULL);
             current = NULL; // this line (among others) is very suspicious
        }
        current = parent;
    }
}

template <class T>
T BinTreeNode<T>::getData() const
{
    return data;
}

template <class T>
void BinTreeNode<T>::setData(T data)
{
    this->data = data;
}

template <class T>
BinTreeNode<T> * BinTreeNode<T>::getLeft() const
{
    return left;
}

template <class T>
BinTreeNode<T> * BinTreeNode<T>::getRight() const
{
    return right;
}

template <class T>
void BinTreeNode<T>::setLeft(BinTreeNode<T> * node)
{
    node->setParent(this);
    left = node;
}

template <class T>
void BinTreeNode<T>::setRight(BinTreeNode<T> * node)
{
    node->setParent(this);
    right = node;
}

template <class T>
BinTreeNode<T> * BinTreeNode<T>::getParent() const
{
    return parent;
}

template <class T>
void BinTreeNode<T>::setParent(BinTreeNode<T> * node)
{
    parent = node;
}

template <class T>
bool BinTreeNode<T>::operator<(const BinTreeNode<T>& node) const
{
        return this->data < node.data;
}

template <class T>
bool BinTreeNode<T>::operator>(const BinTreeNode<T>& node) const
{
    return this->data > node.data;
}

template <class T>
bool BinTreeNode<T>::operator==(const BinTreeNode<T>& node) const
{
    return this->data == node.data;
}

#endif /* BINTREENODE_H_ */

最佳答案

您的 BinTreeNode 析构函数应该只是:

template <class T>
BinTreeNode<T>::~BinTreeNode() {
    delete left;
    delete right;
}

这将递归调用 left 和 right 的析构函数,释放为这些节点及其子节点分配的内存。这将因此释放整棵树。

NULL 分配给指针不会释放它指向的内存。

另一方面,你提到的,删除后,这一行:

node->getData();

仍然返回数据,完全正常。删除会释放内存,但存储在其中的数据可能仍然可用一段时间,直到在该内存地址中写入新内容。访问已经释放的内存地址意味着未定义的行为。

顺便说一句,您应该在 C++ 中使用“0”(不带引号)而不是 NULL。因此,没有必要使用 #ifndef NULL(...)。

编辑:我没有看到“无递归”评论。这是一个非递归算法:

#include <deque>

/* ... */

template <class T>
BinTreeNode<T>::~BinTreeNode() {
    std::deque deq;
    // we're going to delete our children
    deq.push_back(this);
    while(deq.size()) {
        BinTreeNode<T> *ptr = deq.front();
        deq.pop_front();
        if(ptr) {
            deq.push_back(ptr->left);
            deq.push_back(ptr->right);
            // we don't want the child nodes
            // to double delete the children
            ptr->left = 0;
            ptr->right = 0;
            // avoid deleteing ourselves
            if(ptr != this)
                delete ptr;
        }
    }
}

我还没有测试过,但应该可以。

关于c++ - 通用二叉树节点析构函数问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9745736/

相关文章:

python - 使用 Raspberry pi 3、OpenCV 和 Python 的运动跟踪器

c++ - 我是否应该将使用 push_back 的代码更改为使用 std::move?

algorithm - 按频率降序列出以固定前缀开头的 `k` 个单词

python - 如何在Python中的类中而不是类之外声明函数

java - 使用java在特定位置插入节点

algorithm - 是什么使树遍历预序或有序?

java - 为什么/我在哪里陷入无限循环

c - 如何按顺序显示平衡二叉树的下一个元素? (为了)

c++ - 为什么复制构造函数参数是 const?

c++ - 如何创建纹理坐标