【问题标题】:Binary search tree recursive destructor二叉搜索树递归析构函数
【发布时间】:2016-07-31 23:11:27
【问题描述】:

我检查了其他类似的主题,但似乎没有一个对我有帮助。

我正在尝试为此特定的 BST 实现编写析构函数。每个节点都包含一个指向父节点的指针、一个指向左节点的指针、一个指向右节点的指针和一个指向它所包含的值的指针。 这是课程开头的样子:

#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

BST 类的用法如下:

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

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

root->inOrderDisplay();

root->remove(5);

我提到所有方法都可以正常工作(我决定不发布它们,因为它们不是这个问题的主题)。问题是,当我使用 Valgrind 运行我的测试文件时,它会检测到一些内存泄漏,我确信它们的发生是因为缺少适当的析构函数(上述析构函数会产生分段错误)。

编辑:我添加了其他方法的代码

谢谢!

【问题讨论】:

  • 您应该包含add(和remove)的代码...您可能在这些代码中犯了错误...
  • 使用 RAII:std::unique_ptr&lt;BST&lt;T&gt;&gt; left; std::unique_ptr&lt;BST&lt;T&gt;&gt; right; std::unique_ptr&lt;T&gt; value; BST&lt;T&gt;* parent;
  • 我看了两遍,但我找不到您的removeRecursively() 有什么问题,尽管我认为没有必要将您的指针归零。但这也不会造成伤害。
  • 我提到所有方法都可以正常工作 -- 如果我每次听到这个代码都没有发布的地方都有一美元问题,我会成为一个有钱人。您在main 程序中发布了对add 函数的调用。你发帖,我们需要看看。
  • @RudyVelthuis:removeRecursively() 中存在几个问题,包括(但不仅限于)当前答案中指出的问题。

标签: c++ recursion binary-search-tree destructor


【解决方案1】:

你的基本设计有点破,至少在 IMO 上是这样。也就是说,如果你愿意跳过足够多的圈子,你可能可以让它工作,但即使充其量,它也可能总是至少有点笨拙。

我首先为树中的节点定义一个单独的类。然后树本身将持有一个指向树根的指针,并定义树的大部分接口。

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

破坏不必显式递归。 delete(left)(例如)将为左子节点(如果有的话)调用 dtor,然后为其子节点调用 dtor,依此类推。当我们到达一个叶节点时,我们将得到(相当于)delete nullptr;,它被定义为什么都不做,停止递归。

【讨论】:

  • 这是一个综合性的设计。
【解决方案2】:

你应该换行:

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

像这样:

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

如果您首先分配 NULL,则不会删除任何内容。

【讨论】:

  • 我试过了,但析构函数仍然导致段错误。
  • @Polb 到底在哪里?你可能有另一个问题
  • 使用空析构函数使我的代码运行良好,没有段错误,但有 400 KB 的内存泄漏。可能我写的析构函数实现一点都不好。
  • @Polb 您应该添加所有代码并给出确切的错误以及发生的位置
  • 根本不需要 NULL,IMO。
【解决方案3】:

最难找到的错误是您决定不查看代码中的错误,因为您已经说服自己它们不相关。

无论如何,你的实现有一个明显的问题:在删除一堆其他东西之后,你基本上是在做

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

【讨论】:

  • 如果使用空析构函数,BST 会按预期工作,但问题是内存泄漏。
  • 我相信他是在指出,如果该节点的析构函数被调用,那么某些东西已经在删除它。因此,您要删除该节点两次。
  • 使用delete this 如果对象是在堆栈上分配的,或者作为全局或静态的,也不能很好地工作。
【解决方案4】:

将 RAII 与 unique_ptr 一起使用以避免这些问题:

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-12-22
    • 2013-10-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-01-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多