我写了一个程序来测试我的二叉树,当我运行它时,程序似乎崩溃了(btree.exe已经停止工作,Windows正在检查解决方案......)。
当我通过我的调试器运行并将断点放在我怀疑导致它的函数destroy_tree()时,它似乎按预期运行并返回到main函数。反过来,Main从程序返回,但随后光标跳回到destroy_tree()并在其内部重复循环。
最小代码示例位于下方,因此可以立即运行。我的编译器是MinGW,我的调试器是gdb(我正在使用Code :: Blocks)。
#include <iostream>
using namespace std;
struct node
{
int key_value;
node *left;
node *right;
};
class Btree
{
public:
Btree();
~Btree();
void insert(int key);
void destroy_tree();
private:
node *root;
void destroy_tree(node *leaf);
void insert(int key, node *leaf);
};
Btree::Btree()
{
root = NULL;
}
Btree::~Btree()
{
destroy_tree();
}
void Btree::destroy_tree()
{
destroy_tree(root);
cout<<"tree destroyed\n"<<endl;
}
void Btree::destroy_tree(node *leaf)
{
if(leaf!=NULL)
{
destroy_tree(leaf->left);
destroy_tree(leaf->right);
delete leaf;
}
}
void Btree::insert(int key, node *leaf)
{
if(key < leaf->key_value)
{
if(leaf->left!=NULL)
insert(key, leaf->left);
else
{
leaf->left = new node;
leaf->left->key_value = key;
leaf->left->left = NULL;
leaf->left->right = NULL;
}
}
else if (key >= leaf->key_value)
{
if(leaf->right!=NULL)
insert(key, leaf->right);
else
{
leaf->right = new node;
leaf->right->key_value = key;
leaf->right->left = NULL;
leaf->right->right = NULL;
}
}
}
void Btree::insert(int key)
{
if(root!=NULL)
{
insert(key, root);
}
else
{
root = new node;
root->key_value = key;
root->left = NULL;
root->right = NULL;
}
}
int main()
{
Btree tree;
int i;
tree.insert(1);
tree.destroy_tree();
return 0;
}
另外,我打算从Code :: Blocks内置调试器切换到DDD来调试这些问题。我听说DDD可以直观地显示指向对象的指针而不只是显示指针的地址。您认为制作开关有助于解决这些类型的问题(数据结构和算法问题)吗?
答案 0 :(得分:4)
你的destroy_tree()被调用两次,你调用它一次,然后在执行离开析构函数的main()后调用它。
你可能认为它应该工作,因为你检查leaf!= NULL,但是delete没有将指针设置为NULL。所以当第二次调用destroy_tree()时,你的root不是NULL,
答案 1 :(得分:0)
与您的问题没有直接关系(或者可能是),但为结构提供构造函数是一种好习惯。例如:
struct node
{
int key_value;
node *left;
node *right;
node( int val ) : key_val( val ), left(NULL), right(NULL) {}
};
如果这样做,您的代码会变得更简单,因为您在创建节点时无需担心设置指针,并且不可能忘记初始化它们。
关于DDD,它是一个很好的调试器,但坦率地说,调试的秘诀是首先编写正确的代码,所以你不必这样做。 C ++在这方面为您提供了很多帮助(比如使用构造函数),但您必须了解并使用它提供的工具。
答案 2 :(得分:0)
Btree :: destroy_tree在成功核对树之后没有将'root'设置为0。结果,析构函数类再次破坏destroy_tree(),并且您正在尝试销毁已经销毁的对象。
那将是未定义的行为:)。
答案 3 :(得分:0)
一旦你摧毁根。
确保它是NULL,因此它不会再尝试(从析构函数)
void Btree::destroy_tree(node *leaf)
{
if(leaf!=NULL)
{
destroy_tree(leaf->left);
destroy_tree(leaf->right);
delete leaf;
leaf = NULL; // add this line
}
}