C ++析构函数,EXC_BAD_ACCESS,无法访问内存

时间:2013-02-02 18:31:30

标签: c++ destructor

我计划使用排序数组来构建平衡二叉搜索树。一切顺利,直到计划结束。似乎二叉搜索树的析构函数存在一些问题。

错误信息:

  

程序收到信号EXC_BAD_ACCESS,无法访问内存。   原因:地址为KERN_PROTECTION_FAILURE:0x00007fff5f3ffff8   BST_Node ::〜BST_Node(this = 0x100100920)中的0x0000000100002857   BST_Node.h:18~BST_Node(){delete parent;删除左;删除   右;}

#ifndef BST_NODE_H
#define BST_NODE_H

#include <cstddef>

class BST_Node {
    public:
        BST_Node(int k) : key(k), parent(NULL), left(NULL), right(NULL) {}
        ~BST_Node() {if (parent!=NULL) delete parent;if (left!=NULL) delete left;if (right!=NULL) delete right;}
        // data member
        int key;
        BST_Node* parent;
        BST_Node* left;
        BST_Node* right;
};

#endif

#include <iostream>
#include <vector>
#include "BST_Tree.h"
#include "BST_Node.h"

using namespace std;

// use recursion to construct the tree. Find the mid of the array to be the root of the subtree. Use left part of the array to construct the left subtree and right part to construct the right subtree.
BST_Node* CreateTree(vector<int> &a, int start, int end) {
    if (end<start) {
        return NULL;
    } else {
        int mid = (start+end+1)/2;
        BST_Node* p = new BST_Node(a[mid]);
        BST_Node* l_tmp = CreateTree(a, start, mid-1);
        if (l_tmp)
            l_tmp->parent = p;
        p->left = l_tmp;
        BST_Node* r_tmp = CreateTree(a, mid+1, end);
        if (r_tmp)
            r_tmp->parent = p;
        p->right = r_tmp;
        return p;
    }
}

int main() {
    vector<int> a;
    a.push_back(0);
    a.push_back(1);
    a.push_back(2);
    a.push_back(3);
    a.push_back(4);
    a.push_back(5);
    a.push_back(6);
    BST_Tree t;
    t.root = CreateTree(a, 0, 6);
    t.InOrderWalk(t.root);
    return 0;
}

非常感谢!

2 个答案:

答案 0 :(得分:2)

任何所有权关系(当不再需要该对象时,对象负责删除另一个对象)必须没有周期。在您的情况下,您有父节点拥有他们的孩子,反之亦然。

删除根节点后,构造函数将首先删除左子节点。子的析构函数将删除已被删除的父级。

树中的典型所有权关系是父节点拥有其子节点。孩子们有一个指向没有所有权的父母的指针。该指针在子节点的生命周期内仍然有效,因为子节点将作为父节点销毁的一部分被删除。

所以你的析构函数应该只是:

BST_Node::~BST_Node() {
   delete left;
   delete right;
}

附加说明:

  • 您不需要检查NULL指针 - 删除nullpointer是 安全无所事事。
  • 您应该阻止复制BST_Node对象。隐含地 定义的复制构造函数和复制赋值运算符将创建 另一个拥有相同子节点的对象,也会产生 双重删除对象。

表达指针所有权语义的最佳方法是使用合适的智能指针类。在您的情况下,最合适的声明是

class BST_Node {
    public:
        ~BST_Node() = default; // you can simply omit this completely

        // other things

        BST_Node* parent; // no ownership
        std::unique_ptr<BST_Node> left;  // exclusive ownership
        std::unique_ptr<BST_Node> right;
};

作为一种有用的附加效果,隐式生成的复制操作将被抑制,因为std::unique_ptr<T>不可复制。

答案 1 :(得分:0)

只是通过代码运行我在此细分中看到了一个问题

    if (l_tmp)
        l_tmp->parent = p;
    p->left = l_tmp;
    BST_Node* r_tmp = CreateTree(a, mid+1, end);
    if (r_tmp)
        r_tmp->parent = p;
    p->right = r_tmp;

l_tmp->parentr_tmp->parent都指向同一个节点p。因此,一旦调用了l_tmp的析构函数,并且在调用r_tmp的析构函数时再次删除节点p。这可能是您看到错误的原因。 正如Andy在评论中所建议的那样,这似乎是使用智能指针的一个好方案。