C++ Segfault on clearing a binary search tree - c++

I'm trying to eliminate any memory leaks in in my Binary Search tree that I'm making so I made a simple recursive delete method. This is the method that first causes
void BST::copy(const BST& other){
if (other.root != NULL){
Clear();
Which Clear(); will first thing call recursiveDelete(root);
void BST::recursiveDelete(BSTNode * head){
if (head == NULL)
return;
recursiveDelete(head->left);
recursiveDelete(head->right);
delete head;
}
This code will Segfault and I don't know why. When it calls the copy method it only has one node in it, so it says that head is not NULL as expected, but for some reason when it tries to reference head->left it segfaults.
Any help would be greatly appreciated. :)
EDIT: I didn't know this until one of the people who answered pointed it out, but in the copy constructor, one need to initialize everything to NULL first before attempting to copy. So my problem was solved (for the most part) when I changed my constructor to be the following
BST::BST(const BST & other) : root(NULL), size(0){

First comment: You should probably clear the object's data in the copy if the other object has data or not. If the other object is empty, then the copy should make this object empty too.
Second comment: I cannot tell from your question, but I expect that head->left or head->right was not set to NULL properly during construction. An invalid but not NULL value would explain getting past the if (head == NULL) check.

In BST::copy it seems odd that your check for NULL is not on the same object as Clear() is called on. There's not enough code given for me to say for sure, but shouldn't it be:
if (root != NULL) {
Clear();
}
Then you may also need to check other.root != NULL for subsequent code you haven't posted.
But as a general rule, Clear() should be made safe to call either way. You should simply check for NULL inside Clear().

Try after
delete head;
head = NULL;
and make sure left and right are set to NULL in the constructor.

Related

Is this code for emptying a linked list okay?

I need to work on a function that resets a linked list without completely eliminating it, and I wanted to know if my piece of code is right. Any other suggestions?
void reset(){
if (initial == nullptr){
return;
}
Node<T> *flag;
Node<T> *temp;
while(initial->obtainNext() != nullptr){
flag = initial->();
temp = flag->obtainNext();
delete flag;
inicial->setNext(temp);
}
initial->setNext(nullptr);
return;
}
By my understanding you're trying to erase your linked list.
1. You may just loop through the given node and delete all of them setting the head node as nullptr.
2. You may make an object which holds that linked list and may serve your purpose by using method 1 in erasing everybody but the object still exists with a variable set to nullptr.
I suppose you have already implemented pop_back() \ pop_front() \ erase() method in your class so it is possible to not write some new additional logic but use existing one with any loop as followed:
void clear
{
while(this->size) //can be class field which indicates list's size or call size() method
this->pop_front();// can be changed as mentioned above
}

Why Is My Binary Tree Overwriting The Leaves Of Its Root?

I've pinpointed my issue to this specific function, it's the helper function for my binary tree. Before this function call there is a node but instead of growing it seemingly just replaces that node. When I look at my code in my head it all makes sense but I can't figure out what I'm doing wrong.
Here is the function that calls add:
void BSTree::Insert(Client &newClient) {
if (isEmpty())
{
Node *newNode = new Node(newClient);
this->root = newNode;
}
else
add(this->root, newClient);
}
and here is my add() function:
BSTree::Node* BSTree::add(Node *node, Client &newClient) // helper function for Insert()
{
if (node == nullptr)
{
Node *newNode = new Node(newClient);
//node = newNode; // already tried adding this in
return newNode;
}
if (newClient.clientID < node->pClient->clientID)
return node->left = add(node->left, newClient); // already tried just returning add()
else
return node->right = add(node->right, newClient);
}
Since this is your question, I will explain what your code is doing. Imagine you have a mature binary tree already and you are adding a node to your tree. By the time you reach this line
return node->left = add(node->left, newClient);
Three separate instructions are carried out:
newClient is added to the left branch of node by add().
the left child of node is set to the return value of add().
the right hand side (RHS) of the assignment is returned by the parent function.
The issue is with number 2. If the tree you are adding to is mature already, changing left child of nodes as you're traversing the tree will cause the override effect that you're observing. In fact, the problem goes beyond overwriting leaves. Since you use the new keyword, the overwritten nodes still have allocated heap space, are never deleted and cause a memory leak.
Here are some thoughts to get you on the right direction:
Your insert() function ensures that the first time you call add(), you are not passing nullptr as the first argument. Take advantage of that and ensure nullptr is never passed into add() function by checking for nullptr before you do the recursive call. Change the return type of add() to void. You no longer need to check node is nullptr. Here's some pseudocode to guide you
void add(node, val)
if val < node.val
if node.left exists
add(node.left, val)
else
make a new object and set node.left to that object
else
if node.right exists
add(node.right, val)
else
make a new object and set node.right to that object
There is a problem with your logic. First of all, there is the insert() method which you should write like this for better understanding:
void BSTree::Insert(const Client &newClient) // use const to prevent modification
{
if (isEmpty()) { root = new Node(newClient); }
else { add(this->root, newClient); }
}
This way you are creating a new object at root directly with the help of 'root' pointer in BSTree.
Now, about the add() method. The 'node' you are passing as a parameter is a copy of the pointer variable, so the actual pointer value is not changed. See this:
BSTree::Node* BSTree::add(Node *node, Client &newClient) //logical error
You need to pass the Node* by reference like this using 'Node* &node':
BSTree::Node* BSTree::add(Node* &node, const Client &newClient)
Why is you binary tree overwriting the roots of its leaves? Answer:
Your recursive call with return statement is totally wrong.
return node->left = add(node->left, newClient);
The add(node->left, newClient) always returns the address of the leaves, and you are returning this value. It goes for recursive calls until it reaches the leaves place.
Conclusion: Since, there are a lot of bugs, I would suggest you re-write logic again carefully.
I hope this helps! :-)

Linked list destructor that loops forever?

So I'm working on making a linked list, and everything about it is working exactly as I want it to except for the list's destructor:
PhoneList::~PhoneList()
{
PhoneNode *ptr = head;
while (ptr != NULL)
{
head = head->getNext();
delete ptr;
ptr = head;
}
}
When the program runs, it does everything else I need it to, but then seems to loop indefinitely on the destructor when it tries to call it and end the program. This is my first question here, and I'm a bit of a newcomer to this whole programming thing, so if you need to see any more of my program code to make sense of this destructor I will gladly share it!
You would be better off adding a destructor for PhoneNode, such that each each deletes its successor. Then you don't need the loop at all:
PhoneNode::~PhoneNode()
{
delete next;
}
Note that this traverses the entire remaining list.
then your PhoneList destructor just looks like:
PhoneList::~PhoneList()
{
delete head;
}
Take care in your Delete(int id) method that you zero the next pointer of a node you're going to delete if it's in the middle of the list (i.e. it isn't already zero). Otherwise you lose the entire rest of the list.

delete right child makes parent left pointer point to nullptr

thank you for checking my question, I currently have a really basic question on operator "delete", it seems it can automatically change the pointer value to nullptr. Let me give you a example for this:
template <typename T>
void Tree<T>::remove(const unsigned& index, TreeNode<T>*& tree)
{
if(tree == nullptr)
{
std::cerr << "remove: can't find target" << std::endl;
}
else if(index < tree->index)
{
remove(index, tree->left);
}
else if(index > tree->index)
{
remove(index, tree->right);
}
else if(index == tree->index)
{
if(tree->degree() == 2)
{
tree->index = findMin(tree->right)->index;
tree->value = findMin(tree->right)->value;
remove(tree->index, tree->right);
}
else
{
auto oldNode = tree;
tree = (tree->left != nullptr) ? tree->left: tree->right;
delete oldNode;
// oldNode = nullptr;
}
}
}
The code above is a classic searching tree remove algorithm. If the current tree only has two nodes which is root (with key equals to 3 for example) and right child (with key equals to 4 for example), so when I remove node 4, it will call remove twice and go to this line:
delete oldNode;
And this line will delete "oldNode", which should be 4 right now. As far as my knowledge, the delete operator will just free the memory address(the address is same to the value of oldNode), which means it tells OS this address is available again. So I suppose when I print out the value of root's right pointer(root->right), I should get a address. Actually when I print out I get 0. So my question when root->right changed?
Hope I explain my question clearly. That may be a stupid question, let me known if I make any confusion.
I think what you are seeing is that the use of a pointer after the delete is undefined behavior (until c++14).
For c++14: indirection through a pointer that became invalid in this manner and passing it to a deallocation function (double-delete) is undefined behavior. Any other use is implementation-defined.
Undefined behavior basically allows the implementation to do whatever it wants with the pointer after delete (even change its value).
It looks like your implementation sets the value of the pointer to nullptr in delete.
I found the answer, but before answer, let me clarify my question. The question is asking when a child is deleted, when and who set its parent right pointer to nullptr. The answer is this function has pointer reference as argument, when call this function, the passed parameter itself will be set to nullptr. For example, when pass in root->right, the root->right can be directly set to nullptr.

binary search tree assignment operator

I am having a huge issue with my recursive function in my binary search tree. My project is due in a few hours and I cannot for the life of me get ahold of my instructor.
My function seems to only be traversing the left most branch of my tree.
Assignment Operator:
template<typename Type>
BST<Type>& BST<Type>::operator=(const BST& that)
{
if(this != &that)
{
this->clear();
Node *c = that.root;
preORet(c);
}
return *this;
}
Recursive Function Called:
template<typename Type>
void BST<Type>::preORet(Node *c)
{
this->insert(c->data);
if(c->left != nullptr)
preORet(c->left);
else if(c->right != nullptr)
preORet(c->right);
}
As an aside, I understand that a lot of this may look like seriously bastardized code, but this is how my instructor expects it to look.
Thank you in advance.
Your problem is right here:
if(c->left != nullptr)
preORet(c->left);
else if(c->right != nullptr)
preORet(c->right);
You don't want an else if. You want to traverse the right sub-tree regardless of whether the left sub-tree was a nullptr.
In addition to taking out the else in your preORet() function, as the others have pointed out, it's also worth noting that you will get a segmentation fault (error code 11) if you happen to pass in a null pointer through your parameter Node *c.
Here is a solution:
template<typename Type>
void BST<Type>::preORet(Node *c)
{
if (c != nullptr)
{
this->insert(c->data);
preORet(c->left);
preORet(c->right);
}
}
This way, every pointer get's checked to see if it is null before being used, including the left and right pointers. If it is null, it'll just fall through, out of scope.
Get rid of the else in preORet().
Get rid of that else.
As your design looks, at first glance, to be easily move or swap able, I would use the copy-swap idiom to generate a reasonably efficient, easy to write operator=.
But that is just me.