2013-02-02 31 views
3

我打算使用排序数组构造一个平衡二叉搜索树。所有的工作都很好,直到节目结束。似乎二叉搜索树的析构函数有一些问题。C++析构函数,EXC_BAD_ACCESS,无法访问内存

错误信息:

计划接收信号EXC_BAD_ACCESS,无法访问内存。 原因:地址上的KERN_PROTECTION_FAILURE:0x00007fff5f3ffff8 0x0000000100002857 in BST_Node ::〜BST_Node(this = 0x100100920)at 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; 
} 

非常感谢您!

+1

你有没有考虑过使用智能指针?这正是他们想要解决的问题 –

+0

删除'删除父'。这不是如何进行递归调用, – Dan

+0

所以你删除父母,它会运行父母的析构函数,它会删除它的左边和右边的孩子,谁会删除他们的父母。这将是一个问题。 – nos

回答

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在评论中所建议的那样,这似乎是使用智能指针的好方案。

2

任何所有权关系(对象负责删除另一个对象时不再需要的关系)不得有循环。在你的情况下,你有父节点拥有他们的孩子,反之亦然。

当根节点被删除时,它的构造函数将首先删除左边的子节点。孩子的析构函数会删除已经被删除的父代。

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

所以,你的析构函数应该简单地做:

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

其他注意事项:

  • 你并不需要检查NULL指针 - 删除空指针是 安全,不会做任何事情。
  • 您应该防止复制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>是不可拷贝。

+0

哦,我明白了。非常感谢你! – randomp

+0

另一个问题,我应该为家长使用std :: shared_prt 吗? – randomp

+0

不是。任何对象都应该有一个唯一的所有者,也可以有多个共享所有者。你不能混合这些类型的所有权。对于共享所有权,您仍然必须小心“拥有”关系没有周期。否则就会有内存泄漏:对象将彼此拥有,并且它们中的任何一个都不会成为第一个被销毁的对象(因为它没有更多的拥有者)。在别处看到我的其他评论。 – JoergB