Read access violation on destroying a binary search tree - c++

Good evening.
I get an access violation exception error when trying to destroy my BST.
There have been posts about this before and I copied their accepted answer's reply and still didn't get the expected result.
So I have this binary search tree implementation. Everything works well, until my code reaches the "return 0" from my int main() function.
I'll leave some code for you.
PQBST::~PQBST()
{
destroy();
}
inline void PQBST::destroy()
{
if (root)
destroy(root);
}
void PQBST::destroy(Node* node)
{
if (node->left) // this is where it gives me and access violation exception 0xDDDDDDDD
destroy(node->left);
if (node->right)
destroy(node->right);
delete node;
}
I know that this kind of error is thrown when you try to delete something that had been already deallocated, but I can't figure out why it would try to destroy my BST twice when I call the destroy function just once in my application (when I'm done with it).
I commented the part where I manually destroy my BST, and after reaching "return 0", it gives me again
Unhandled exception thrown: read access violation.
node was 0xFF12C6AB
so it is not 0xDDDDDDDD but still an error . :|
My nodes look like this:
struct Node
{
Human info;
Node * left;
Node * right;
Node() {};
Node(Human value)
: info(value), left(NULL), right(NULL)
{
}
};
My BST class only has Node* root .
I hope I gave you enough information.
Thank you.
Edit: My nodes look like this now:
struct Node
{
Human info;
Node * left;
Node * right;
Node() { left = NULL, right = NULL; }
Node(Human value): info(value), left(NULL), right(NULL){}
Node(Human value, Node* left, Node* right) : info(value), left(left), right(right) {}
Node& operator=(const Node& n)
{
info = n.info;
left = n.left;
right = n.right;
return *this;
}
Human getInfo() const { return info; }
Node* getLeft() { return left; }
Node* getRight() { return right; }
~Node() { };
};
My PQBST:
class PQBST
{
private:
Node * root;
int m; //spaceship size - given by the user
public:
PQBST() { root = NULL; }
PQBST(int m) : root(NULL), m(m) {}
PQBST(Node* root, int m);
~PQBST();
PQBST::PQBST(Node * root, int m)
{
this->root = root;
this->m = m;
}

If you are going do delete your BST I suggest doing a post order traversal. This would visit the left, right recursively and then the delete the node. This would work provided the tree was constructed correctly.
void PQBST::destroy(Node* node)
{
if (!node) return;
destroy(node->left);
destroy(node->right);
delete node;
}
Having said that, I would suggest moving away from trying to manually manage memory and use std::unique_ptr<T> where T is your node type. I would define them in your BST as std::unique_ptr<Node> left, right;. This avoids the problem altogether.

I know that this kind of error is thrown when you try to delete something that had been already deallocated, but I can't figure out why it would try to destroy my BST twice when I call the destroy function just once in my application (when I'm done with it).
Not sure if I interpret what you said above (in bold) correctly, but aren't you calling PQBST::destroy() already in PQBST::~PQBST()? If you call PQBST::destroy() also manually, would it be called twice when the destructor is called?

Related

Bad access of memory - exception (C++)

I get EXC_BAD_ACCESS (code=1, address=0x0) on the 3rd last line where I return a *Bitvector. As you can see in the main I try to flip the 10'th index to 1. I highly suspect the way I manage my memory is the cause, but can't figure out where and how. (Note that the BinTree is not completed, I'm using a mac, and I use vs-code).
struct node {
BitVector b;
node* parent;
node* right;
node* left;
};
class BinTree {
node* root;
node* insert(node* t, int g) {
if (t==NULL) {
t->b.get_bitvector()->at(g) = 1; //Here I try to set the g'th index to be 1 in the bitvector.
}
return t;
}
public:
BinTree() {
root = NULL;
}
void insert(int g) {
root = insert(root, g);
}
};
class BitVector {
vector<short>* bitvector; // construct a pointer
public:
BitVector() {
bitvector = new vector<short>(30); // point the pointer to a vector of size 30.
}
vector<short>* get_bitvector() {
return bitvector; //Exception occurs here
}
};
int main() {
BinTree t;
t.insert(10);
}
EXC_BAD_ACCESS happens when you try to access memory which isn't allocated by the application; which the application has no access privilege to.
I suppose the error is because your not adding nodes to your tree. Your just calling them. And over here: if (t==NULL), t->b.get_bitvector()->at(g) is called only if the node address is 0x0, hence the error.
What you should do is, make the logic to add a node if not found in some way and change t==NULL to t!=NULL.
Note: Try using nullptr and ditch NULL when working with pointers.
Your program is failing at two points.
First, you have to check bintree->t for being NOT null:
if (t != NULL) {
t->b.get_bitvector()->at(g) = 1;
}
Second, you have to initialize your bintree->t with a bitvector instead of a NULL:
public:
BinTree() {
root = new BitVector();
}

Debugger Throwing nullptr Exception During nullptr Check

While going through runtime, I'm getting a nullptr exception while this code is executing.
bool Tree::Insert(int n)
{
if (root == nullptr) // This is where it throws
{
Node* root = new Node(n);
return true;
}
Initialization in Tree.h
private:
Node* root;
and Tree constructor.
Tree::Tree()
{
root = nullptr;
}
I have coded exactly like this before and it never threw an exception.
UPDATE:
I apologize for the confusion on the extra '}' Tree::Insert(). There's more code in there and they all have a return case. I had this before
Node* newNode = new Node(n);
root = newNode;
but changed it for a different reason.
Node.h
#pragma once
struct Node
{
int data;
Node *left;
Node *right;
// Constructor
Node() { data = 0; left = nullptr; right = nullptr; }
// Parameterized
Node(int d) { data = d; left = nullptr; right = nullptr; }
// Destructor
~Node() { data = 0; }
};
You either access the Tree::Insert member directly (i.e. not via an instance of the Tree class as if it was a static member) or the instance of the Tree class you're using to access the Insert method is not initialized.
In other words, this is null.
nullptr is implementation-specific. when you say,
"I have coded exactly like this before and it never threw an exception."
you may have to check if it was on a different compiler.

C++ pointers not working?

I have a problem with working with c++ pointers. I'm trying to code a splay tree by using a Node struct and Tree struct. However, upon testing, I have encountered a problem. The portion of my code that's not working is below:
struct Node {
Node* l, *r, *p;
int v;
Node() {}
Node(int _v, Node* _p) : v(_v), p(_p) {}
};
struct Tree {
Node* root;
Tree() : root(0) {}
//...
void insert(int k) {
if (!root) {
root = new Node(k, 0);
return;
}
Node* cur = new Node();
cur->v = root->v;
while (1) {
int x = cur->v;
cout << x << endl;
return;
if (k <= x) {
//cout << x << endl;
//return;
if (!cur->l) {
cur->l = new Node(k, cur);
//splay(cur->l);
return;
} else cur = cur->l;
} else {
if (!cur->r) {
cur->r = new Node(k, cur);
//splay(cur->r);
return;
} else cur = cur->r;
}
}
}
//...
};
int main() {
Tree t = Tree();
t.insert(1);
t.insert(5);
return 0;
}
First, I inserted a node with value 1 in the tree; since there was no root, the tree assigned its root as a new node with value 1. Then, when I inserted 5 into the tree, something weird happened. If you leave the code like it is (keeping the first cout), then it will print out 1 for x. However, if you comment out the first cout and return and uncomment the second cout and return, you'll find that it prints out a random garbage number for x, even though no modifications were made. Can somebody tell me what's wrong?
C++ does not initialize class members automatically.
struct Node {
Node* l, *r, *p;
int v;
Node() {}
Node(int _v, Node* _p) : v(_v), p(_p) {}
};
When you create a new node in your code C++ allocates a piece of memory for the Node but it will not clear it. So the values of l, r & p will be whatever was there.
In your algorithm the tests: if (!cur->r) & (!cur->l) currently fail because there is uninitialized garbage in the nodes and not NULL.
As a result when you try to insert the second node the algorithm thinks that there is a valid node to the right of root. And tries to read the memory there and the value there which is the junk x you see. Depending on the value of the junk it may also crash for some people running the code :)
Also I'm 99.9% certain that Node* cur should be a pointer to a Node in the tree and not a new node so:
Node* cur = new Node(); cur->v = root->v; is wrong and should be Node* cur = root;
Proper Initialization -
In c++11 you can do:
struct Node {
Node* l = nullptr;
Node *r = nullptr;
Node *p = nullptr;
int v = 0;
Node() {}
Node(int _v, Node* _p) : v(_v), p(_p) {}
};
Otherwise
struct Node {
Node* l;
Node *r;
Node *p;
int v;
Node() : l(NULL), r(NULL), p(NULL), v(0){}
Node(int _v, Node* _p) : l(NULL), r(NULL), p(_p), v(_v) {}
};
You should initialize members of a class in the same order they were defined.
Now there are a lot of other things that are problematic in the code:
Tree seems to allocate lots of nodes but does not release any memory. (easiest to just use unique_ptr for l and r and root Node)
Is tree the owner of subnodes? Or should it be Node owning and allocating left and right? (goes away if you use std::unique_ptr for left and right)
You are not initializing the members in the order they are defined. This can cause all kind of errors. (since the compiler reorders initialization without telling you)
Node and Tree handle raw pointers but do not define a proper operator=, copy ctor (or delete them) (goes away if you use unique_ptr)
Tree is missing a dtor to clean allocated memory (goes away if you use unique_ptr)

Deep Copy Constructor for binary tree

I am trying to create a deep copy of my binary tree data structure in C++. The problem is the code I am using only seems to be giving me a shallow copy (which seems to cause problems with my deconstructor).
the code below is my binary tree copy constructor:
BinaryTreeStorage::BinaryTreeStorage(const BinaryTreeStorage &copytree):root(NULL)
{
root = copytree.root;
copyTree(root);
}
BinaryTreeStorage::node* BinaryTreeStorage::copyTree(node* other)
{
//if node is empty (at bottom of binary tree)
/*
This creates a shallow copy which in turn causes a problem
with the deconstructor, could not work out how to create a
deep copy.
*/
if (other == NULL)
{
return NULL;
}
node* newNode = new node;
if (other ->nodeValue == "")
{
newNode ->nodeValue = "";
}
newNode->left = copyTree(other->left);
newNode->right = copyTree(other->right);
return newNode;
}
Any help would be appreciated.
Thanks
Here is the deconstructor that throws a memory exception (which i believe is because of the shallow copy i do above)
BinaryTreeStorage::~BinaryTreeStorage(void)
{
try
{
destroy_tree();//Call the destroy tree method
delete root;//delete final node
}
catch(int &error)
{
cout << "Error Message : " << error << endl;
}
}
void BinaryTreeStorage::destroy_tree()
{
destroy_tree(root);
}
void BinaryTreeStorage::destroy_tree(node *leaf)
{
if(leaf!=NULL)
{
//Recursively work way to bottom node
destroy_tree(leaf->left);
destroy_tree(leaf->right);
//delete node
delete leaf;
}
}
You're not performing a deep copy of your root node, only its children.
Shouldn't it be:
root = copyTree(copytree.root);
?
EDIT: In addition, you destroy the root twice:
destroy_tree();//Call the destroy tree method
//once here
//remove this line
delete root;//delete final node
and
if(leaf!=NULL)
{
//Recursively work way to bottom node
destroy_tree(leaf->left);
destroy_tree(leaf->right);
//one more time here
delete leaf;
}
If you only do one of these fixes, the problem won't be solved.
Actually, I think we can directly using the copy constructor to recursively deep copy the tree. Suppose the class is defined as follows:
class TreeNode {
public:
TreeNode() : value(), count(0), left(nullptr), right(nullptr) {}
TreeNode(const TreeNode &);
~TreeNode();
TreeNode &operator=(const TreeNode &);
// Other members...
private:
std::string value;
int count;
TreeNode *left;
TreeNode *right;
// Note that the valid state for the `left` and `right` pointer is either
// `nullptr` or a subtree node. So that we must check these pointers every
// time before dereferencing them.
};
Then the copy constructor is
TreeNode::TreeNode(const TreeNode &n)
: value(n.value), count(n.count), left(nullptr), right(nullptr) {
if (n.left)
left = new TreeNode(*n.left); // recursively call copy constructor
if (n.right)
right = new TreeNode(*n.right); // recursively call copy constructor
}
The recursion will be stopped at the leaf node, since both its two children are nullptr.
And so does the destructor.
TreeNode::~TreeNode() {
delete left; // recursively call destructor on left subtree node
delete right; // recursively call destructor on right subtree node
}
When left or right is nullptr, delete will do nothing, so that the recursion will be stopped.
You can see the complete code from here.

delete in binary search tree C++ (tree won't update) and heap corruption

I am trying to write a node deletion for a binary tree. These are my node and tree structures:
class node{
public:
int value;
node* left;
node* right;
~node();
};
class tree{
public:
node* root;
....
};
And this is the function I wrote:
void tree::del(node** r, int x){
if(*r)
{
if((*r)->value==x)
{
if(!(*r)->left)
*r= (*r)->right;
else if(!(*r)->right)
*r= (*r)->left;
else
{
int k= delMax((*r)->left);
(*r)->value= k;
}
}
else if((*r)->value > x)
{
node* k= (*r)->left;
del(&k, x);
}
else
{
node* k= (*r)->right;
del(&k, x);
}
}}
My problem is that once I get to the desired element, the pointers change but then when the tree is rebuilt recursively it goes back to what it was originally and no element is deleted. I thought passing a pointer to the pointer would solve this but it didn't. delMax deletes the maximum element from the tree and it works correctly on its own.
Also, in the destructors for the last two classes, how should I place the deletes? because if I just put delete right; delete left; in ~node() and delete root in ~tree() I get an error that I'm corrupting the heap.
Thanks!
By making a local variable k and passing its address, the assignments through *r affect the local variable rather than any pointer in the tree.
As an aside, writing node *&r might save you several & and *s.