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

C++ C++;析构函数EXC_BAD_ACCESS无法访问内存,c++,destructor,C++,Destructor,我计划使用一个排序数组来构造一个平衡的二叉搜索树。在项目结束前,一切都很顺利。二叉搜索树的析构函数似乎有一些问题 错误信息: 程序接收到信号EXC_访问错误,无法访问内存。 原因:地址0x00007fff5f3ffff8处的内核保护失败 BST_节点中的0x0000000100002857::~BST_节点(此=0x100100920)位于 BST_Node.h:18~BST_Node(){delete parent;delete left;delete 对;} \ifndef BST\u节点

我计划使用一个排序数组来构造一个平衡的二叉搜索树。在项目结束前,一切都很顺利。二叉搜索树的析构函数似乎有一些问题

错误信息:

程序接收到信号EXC_访问错误,无法访问内存。 原因:地址0x00007fff5f3ffff8处的内核保护失败 BST_节点中的0x0000000100002857::~BST_节点(此=0x100100920)位于 BST_Node.h:18~BST_Node(){delete parent;delete left;delete 对;}

\ifndef BST\u节点
#定义BST_节点
#包括
类BST_节点{
公众:
BST_节点(intk):键(k)、父节点(NULL)、左节点(NULL)、右节点(NULL){}
~BST_Node(){if(parent!=NULL)删除父节点;if(left!=NULL)删除左节点;if(right!=NULL)删除右节点;}
//数据成员
int键;
BST_节点*父节点;
BST_节点*左;
BST_节点*右侧;
};
#恩迪夫
#包括
#包括
#包括“BST_Tree.h”
#包括“BST_Node.h”
使用名称空间std;
//使用递归构造树。找到数组的中间部分作为子树的根。使用数组的左部分构造左子树,右部分构造右子树。
BST_节点*CreateTree(向量&a、整数开始、整数结束){
如果(endparent=p;
p->左=l_tmp;
BST_Node*r_tmp=CreateTree(a,mid+1,end);
如果(r_tmp)
r_tmp->parent=p;
p->right=r\u tmp;
返回p;
}
}
int main(){
载体a;
a、 推回(0);
a、 推回(1);
a、 推回(2);
a、 推回(3);
a、 推回(4);
a、 推回(5);
a、 推回(6);
BST_树t;
t、 root=CreateTree(a,0,6);
t、 顺序遍历(t.root);
返回0;
}

非常感谢!

通过运行代码,我在这一部分看到了一个问题

l\u tmp->parent
r\u tmp->parent
都指向同一个节点
p
。因此,当调用l\u tmp的析构函数和r\u tmp的析构函数时,会双重删除节点p。这可能是您看到错误的原因。
正如Andy在评论中所建议的,这似乎是使用智能指针的一个好方案。

任何所有权关系(当不再需要某个对象时,该对象负责删除另一个对象)都不能有循环。在您的情况下,父节点拥有其子节点,反之亦然

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

树中的典型所有权关系是父节点拥有其子节点。子节点具有指向不具有所有权的父节点的指针。该指针在子节点的生存期内保持有效,因为子节点将作为父节点销毁的一部分被删除

因此,析构函数只需执行以下操作:

BST_Node::~BST_Node() {
   delete left;
   delete right;
}
补充说明:

  • 您不需要检查空指针-删除空指针是不必要的 安全,什么也不做
  • 应防止复制BST_节点对象 定义的复制构造函数和复制分配运算符将创建 另一个拥有相同子节点的对象,也会导致 双重删除对象
表示指针所有权语义的最佳方法是使用合适的智能指针类

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;
};
类BST\u节点{
公众:
~BST_Node()=default;//您只需完全忽略它即可
//其他事情
BST_节点*父节点;//无所有权
std::unique_ptr left;//独占所有权
std::唯一的ptr权限;
};

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

您考虑过使用智能指针吗?这正是它们要解决的问题。Remove
delete parent
。这不是递归调用的方式,所以您正在删除父级,该父级将运行父级析构函数,该析构函数将删除其左、右子级,每个子级将删除其父级。这将是一个问题。您正在尝试析构函数“parent”2x或尝试访问已析构函数“parent”的成员。BST_树的析构函数是如何工作的?@Andy Prowl:这里的关键问题是要看到
父级
是一个不拥有的指针。盲目地用智能指针替换每个指针将导致相同的问题(使用
唯一的
)或内存泄漏(使用
共享的
).对父指针使用
弱\u ptr
有点过分:树不变量(
x->left->parent==x
,等等)保证
parent
始终有效。哦,我明白了。非常感谢!另一个问题,我应该为parent使用std::shared\u prt吗?不。任何对象都应该有一个唯一的所有者,或者可能有多个共享所有者。您不能混合使用这些类型的所有权。对于共享所有权,您仍然必须注意“所有者”关系没有循环。否则会出现内存泄漏:对象将相互拥有,其中没有一个可以成为第一个被销毁的对象(因为它没有更多的所有者)。请参阅我在别处的其他注释。
BST_Node::~BST_Node() {
   delete left;
   delete right;
}
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;
};