开发者

Why does my C++ code fail to delete all nodes in my BST?

开发者 https://www.devze.com 2022-12-20 00:51 出处:网络
This is supposed to tra开发者_如何学Goverse a BST and delete every node, including the root node.However, at the end, I get the message \"root still has a left node.\"Why aren\'t all nodes deleted?

This is supposed to tra开发者_如何学Goverse a BST and delete every node, including the root node. However, at the end, I get the message "root still has a left node." Why aren't all nodes deleted?

void deleteTree()
{   
    deleteNode(root);
    if(root->right)
        cout << "root still has a right node" << endl;
    if(root->left)
        cout << "root still has a left node" << endl;
    root = 0;

}   

void deleteNode(node *p) 
{   
    if(p->left) 
    {   
        deleteNode(p->left);
        p->left = 0;
    }   
    if(p->right) 
    {   
        deleteNode(p->right);
        p->right = 0;
    }   

    cout << "Deleting node containing " << p->data << endl;
    delete p;
}   


Your are deleting p at the end (root) and then trying to access its contents in deleteTree(), where root no longer points to allocated memory. The result is going to be undefined.


You're deleting root. And then your code is trying to access memory where it was.

You're well into undefined-behaviour land there.


You should not dereference root after you delete it in deleteNode. Use a debugger to inspect why root->left is non-null.


You are looking at root->left after you've already deleted root, making it avalable for use in a new allocated block.


I would simply change the tree itself, it would be easier to deal with it then:

struct Node
{
  Node(data_type data): mLeft(), mRight(), mData(data) {}
  Node(const Node& rhs): mLeft(), mRight(), mData(rhs.mData)
  {
    if (rhs.mLeft.get()) mLeft.reset(new Node(*rhs.mLeft));
    if (rhs.right.get()) mRight.reset(new Node(*rhs.mRight));
  }
  Node& operator=(Node rhs)
  {
    this->swap(rhs);
    return *this;
  }
  ~Node() { }

  void swap(Node& rhs)
  {
    using std::swap;
    swap(mLeft, rhs.mLeft);
    swap(mRight, rhs.mRight);
    swap(mData, rhs.mData);
  }

  Node* left() const { return mLeft.get(); }
  void left(std::auto_ptr<Node> node) { mLeft= node; }

  Node* right() const { return mRight.get(); }
  void right(std::auto_ptr<Node> node) { mRight = node; }

  data_type& data() { return mData; }
  const data_type& data() const { return mData; }

private:
  std::auto_ptr<Node> mLeft;
  std::auto_ptr<Node> mRight;
  data_type mData;
};

By being Object-Oriented, each Node is now responsible for the memory it handles. Also, using std::auto_ptr in the interface makes it clear that it takes ownership.

Note that it's been tailored for deep-copying, any other approach requiring boost::shared_ptr or equivalent. And yes std::auto_ptr leaves you dealing with copying by yourself, no magic there.

This design is much cleaner than using a plain C-struct with everyone being able to manipulate the resources. You still have full access to the underlying data via the accessor... but THEY take care not to invoke undefined behavior...

Of course, you can still crash it down:

Node& node = ...
delete node.left(); // haha

But if C++ may protect against unintended issues, it leaves the door open to evil code.

0

精彩评论

暂无评论...
验证码 换一张
取 消