Advice on resolving memory leak with "new" keyword - c++

I have a question on how to resolve a memory leak for an assignment in my c++ class. In this assignment, I am supposed to implement a BST. I have done so and functionality wise, everything works as I expected. However, I am have a memory leak in my program. The only problem is, when I try to debug my memory leak with Valgrind, I get pointed to a line in my compiled code that I did not write. In the function below, and I did not write this function, this was given to me by my professor, Valgrind points to the line NodeData* ptr = new NodeData(s); in the function below:
void buildTree(BinTree& T, ifstream& infile) {
string s;
for (;;) {
infile >> s;
cout << s << ' ';
if (s == "$$") break; // at end of one line
if (infile.eof()) break; // no more lines of data
NodeData* ptr = new NodeData(s); // NodeData constructor takes string
// would do a setData if there were more than a string
bool success = T.insert(ptr);
if (!success)
delete ptr; // duplicate case, not inserted
}
}
In the driver file for my BST, a file created by my professor, he is allocating NodeData pointers. The NodeData class was also given to me by the professor, but the problem (I think) is that the destructor for the NodeData class is empty. Here is the .cpp file for the NodeData class:
#include "nodedata.h"
//------------------- constructors/destructor -------------------------------
NodeData::NodeData() { data = ""; } // default
NodeData::~NodeData() { } // needed so strings are deleted properly
NodeData::NodeData(const NodeData& nd) { data = nd.data; } // copy
NodeData::NodeData(const string& s) { data = s; } // cast string to NodeData
//------------------------- operator= ----------------------------------------
NodeData& NodeData::operator=(const NodeData& rhs) {
if (this != &rhs) {
data = rhs.data;
}
return *this;
}
//------------------------- operator==,!= ------------------------------------
bool NodeData::operator==(const NodeData& rhs) const {
return data == rhs.data;
}
bool NodeData::operator!=(const NodeData& rhs) const {
return data != rhs.data;
}
//------------------------ operator<,>,<=,>= ---------------------------------
bool NodeData::operator<(const NodeData& rhs) const {
return data < rhs.data;
}
bool NodeData::operator>(const NodeData& rhs) const {
return data > rhs.data;
}
bool NodeData::operator<=(const NodeData& rhs) const {
return data <= rhs.data;
}
bool NodeData::operator>=(const NodeData& rhs) const {
return data >= rhs.data;
}
//------------------------------ setData -------------------------------------
// returns true if the data is set, false when bad data, i.e., is eof
bool NodeData::setData(istream& infile) {
getline(infile, data);
return !infile.eof(); // eof function is true when eof char is read
}
//-------------------------- operator<< --------------------------------------
ostream& operator<<(ostream& output, const NodeData& nd) {
output << nd.data;
return output;
}
Its pretty short, but you can see that the destructor is empty. I dont know if I am allowed to edit the destructor of NodeData to resolve this memory leak and I am not sure how/where else in my code I can delete these pointers (as I cannot edit the driver file itself either). This isnt something I have ever dealt with before and what I need help with. Can someone help me out with this and give me suggestions on ways that I can investigate on how to resolve this memory leak?
Is it possible to resolve the memory leak caused by not deleting the pointers created by: NodeData* ptr = new NodeData(s); in the function the pointers are being passed to (the insert function. I can provide this code if needed).
If it is not possible to resolve the memory leak inside the insert function, is there another place I can feasibly fix it? I think I might be able to edit the NodeData class/make a destructor, but am not totally sure on how to implement such a destructor function in the NodeData class.
And as it is probably helpful, here is the insert function of the BinTree class that is referenced by the buildTree function:
bool BinTree::insert(NodeData* nodeData){
if(root != nullptr){ //if the root of the tree is not NULL, meaning that the BST object exists, we call our insertHelp function.
insertHelp(this->root, nodeData);
return true;
}
else{ //If there is no root node created in our tree, we create a root node.
root = new Node;
root->left = nullptr;
root->right = nullptr;
root->data = nodeData;
return true;
}
return true;
}
// ---------------------------------insertHelp--------------------------------------------------
// Description: Private function that recursively calls itself in order to find the correct location in a BinTree object to add a new node.
// ---------------------------------------------------------------------------------------------------
bool BinTree::insertHelp(Node *nodePointer, NodeData* nodeData){
if(*nodeData < *nodePointer->data){ //Here we are deciding if we need to traverse left or not.
if(nodePointer->left != nullptr){ //If there is a node to the left, we call insertHelper again but on this node to the left.
insertHelp(nodePointer->left, nodeData);
}
else{ //if there is no node the left, and we need to go left, we create a new left node.
nodePointer->left = new Node;
nodePointer->left->left = nullptr;
nodePointer->left->right = nullptr;
nodePointer->left->data = nodeData;
return true;
}
}
else if(*nodeData > *nodePointer->data){//Here we are deciding if we need to traverse right or not.
if(nodePointer->right != nullptr){ //if there is no node the right, and we need to go left, we create a new left node.
insertHelp(nodePointer->right, nodeData);
}
else{ //if there is no node the right, and we need to go right, we create a new right node.
nodePointer->right = new Node;
nodePointer->right->left = nullptr;
nodePointer->right->right = nullptr;
nodePointer->right->data = nodeData;
return true;
}
}
else{ //catch all case. If something goes wrong, return false.
return false;
}
return true;
}
Here is the current destructor for BinTree:
BinTree::~BinTree(){
makeEmpty();
}
void BinTree::makeEmpty(){
//You make a tree empty by deleting all notes in a post-order traversal.
postOrderDeleteNode(this->root); //I will call my private postOrderDelete helper function. I broke things up this way to make the code cleaner.
}
// ---------------------------------postOrderDeleteNode--------------------------------------------------
// Description: This is the private function that performs a post-order traversal of the nodes in a BinTree
// and deletes each node by deallocating the memory assigned when each node is created.
// ---------------------------------------------------------------------------------------------------
void BinTree::postOrderDeleteNode(const Node *rootNode){
if(rootNode == nullptr){ //Base case, we have an empty tree at this node
return;
}
else{
postOrderDeleteNode(rootNode->left); //First we delete the left side of the tree
postOrderDeleteNode(rootNode->right); //Then we delete the right side of the tree.
delete rootNode; //We finally delete the root of the entire BST.
this->root = nullptr;
}
}
And here is the .h file for BinTree:
// ------------------------------------------------bintree.h-------------------------------------------------------
//
// Programmer Name: Aviv Weinstein
// Course Section Number: CSS 502 A
// Creation Date: 1/17/21
// Date of Last Modification: 1/27/21
// Instructor Name: Professor Dong Si
// --------------------------------------------------------------------------------------------------------------------
// Purpose -
// --------------------------------------------------------------------------------------------------------------------
// Notes on specifications, special algorithms, and assumptions:
// --------------------------------------------------------------------------------------------------------------------
#ifndef Bintree_H
#define Bintree_H
#include "nodedata.h"
//We have inlcuded iostream in nodedata.h
using namespace std;
class BinTree{
friend ostream& operator<<(ostream& out, const BinTree& T); //Used for output printing of BinTree objects. To display the tree using inorder traversal.
public:
BinTree(); // constructor
BinTree(const BinTree &); // copy constructor
~BinTree(); // destructor, calls makeEmpty
bool isEmpty() const; // true if tree is empty, otherwise false
void makeEmpty(); // make the tree empty so isEmpty returns true
BinTree& operator=(const BinTree &); //The assignment operator (=) to assign one tree to another
bool operator==(const BinTree &) const; //Boolean comparison operator for equal
bool operator!=(const BinTree &) const; //Boolean comparison operator for NOT equal
bool insert(NodeData*); //inserts a new node, with NodeData, into the BinTree object.
bool retrieve(const NodeData &, NodeData* &) const; //Looks for a specific node in the BinTree. Returns true if the node exists.
void displaySideways() const; // provided below, displays the tree sideways
int getHeight(const NodeData &) const; //function to find the height of a given value in the tree.
void bstreeToArray(NodeData* []); //function to fill an array of Nodedata* by using an inorder traversal of the tree
void arrayToBSTree(NodeData* []); //function to fill an array of Nodedata* by using an inorder traversal of the tree
private:
struct Node {
NodeData* data; // pointer to data object
Node* left; // left subtree pointer
Node* right; // right subtree pointer
};
Node* root; // root of the tree
//Utility functions
void sideways(Node*, int) const; //provided below, helper for displaySideways()
bool insertHelp(Node *nodePointer, NodeData* nodeData); //Helper function for the insert function.
void postOrderDeleteNode(const Node *node); // Helper function. Deletes all nodes in a BinTree object
void inorderHelper(Node *startNode) const; //Helper function for printing out all nodes in a BST using in-order traversal
void inorderHelperArray(NodeData* a[], Node *startNode) const; //Helper function for the operator == and operator!= functions.
//Used to compare BSTs to each other using in-order traversal.
Node* retrieveHelper(Node *root, const NodeData &nodeData) const; //A hlper function for retriving a node in a BinTree
int getHeightUtil(Node *node)const; //Helper for performing a recursive calculation of the height of a node in a BinTree.
void preorderTraversal(Node* node); //Performs a pre-order traversal of a BinTree object. Called by the operator= function.
void convert(NodeData* a[], int start, int end, Node *root); //Performs a utility function in the arraytoBSTree function.
//Selects the correct array indexes to be added next into an array.
};
#endif

Every time you call new, there must be corresponding delete for it. In your example, you call new, then store node in BinTree. I think you do not delete this node from BinTree once tree gets deallocated. Storing std::shared_ptr instead of raw pointer will resolve this problem.
It is always better to use automatic memory management
void buildTree(BinTree& T, ifstream& infile) {
string s;
for (;;) {
infile >> s;
cout << s << ' ';
if (s == "$$") break; // at end of one line
if (infile.eof()) break; // no more lines of data
// ptr will be removed automatically when it goes out of scope
std::shared_ptr<NodeData> ptr = std::make_shared<NodeData>(s);
// would do a setData if there were more than a string
// tree insertion would increase ptr scope to the scope of a T instance,
// so if T is deallocated, all nodes will be deallocated automatically
T.insert(ptr);
}
}
If, for some reason, you do not want/can't use shared_ptr, then change nothing in buildTree and make BinTree::insert convert raw pointer to std::shared_ptr inside:
bool BinTree::insert(NodeData* nodeData){
if(root != nullptr){ //if the root of the tree is not NULL, meaning that the BST object exists, we call our insertHelp function.
insertHelp(this->root, nodeData);
return true;
}
else{ //If there is no root node created in our tree, we create a root node.
root = new Node;
root->left = nullptr;
root->right = nullptr;
// update ---vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
root->data = std::shared_ptr<NodeData>(nodeData);
return true;
}
return true;
}
bool BinTree::insertHelp(Node *nodePointer, NodeData* nodeData){
if(*nodeData < *nodePointer->data){ //Here we are deciding if we need to traverse left or not.
if(nodePointer->left != nullptr){ //If there is a node to the left, we call insertHelper again but on this node to the left.
insertHelp(nodePointer->left, nodeData);
}
else{ //if there is no node the left, and we need to go left, we create a new left node.
nodePointer->left = new Node;
if(!nodePointer->left) return false;
nodePointer->left->left = nullptr;
nodePointer->left->right = nullptr;
nodePointer->left->data = std::shared_ptr<NodeData>(nodeData);
return true;
}
}
else if(*nodeData > *nodePointer->data){//Here we are deciding if we need to traverse right or not.
if(nodePointer->right != nullptr){ //if there is no node the right, and we need to go left, we create a new left node.
insertHelp(nodePointer->right, nodeData);
}
else{ //if there is no node the right, and we need to go right, we create a new right node.
nodePointer->right = new Node;
if(!nodePointer->right) return false;
nodePointer->right->left = nullptr;
nodePointer->right->right = nullptr;
nodePointer->right->data = std::shared_ptr<NodeData>(nodeData);
return true;
}
}
else{ //catch all case. If something goes wrong, return false.
return false;
}
return true;
}

This comes down to a bug in your insertHelp and insert functions as well as the fact that you do not delete the data object of your node.
If you take a close look at insert, you'll notice that it always returns true so even if you attempt to insert a duplicate, buildTree never deletes that object.
// we need to take the return values of the recursive calls in this function
bool BinTree::insertHelp(Node* nodePointer, NodeData* nodeData) {
if (*nodeData < *nodePointer->data) { //Here we are deciding if we need to traverse left or not.
if (nodePointer->left != nullptr) { //If there is a node to the left, we call insertHelper again but on this node to the left.
return insertHelp(nodePointer->left, nodeData);
}
else { //if there is no node the left, and we need to go left, we create a new left node.
nodePointer->left = new Node;
nodePointer->left->left = nullptr;
nodePointer->left->right = nullptr;
nodePointer->left->data = nodeData;
return true;
}
}
else if (*nodeData > *nodePointer->data) {//Here we are deciding if we need to traverse right or not.
if (nodePointer->right != nullptr) { //if there is no node the right, and we need to go left, we create a new left node.
return insertHelp(nodePointer->right, nodeData);
}
else { //if there is no node the right, and we need to go right, we create a new right node.
nodePointer->right = new Node;
nodePointer->right->left = nullptr;
nodePointer->right->right = nullptr;
nodePointer->right->data = nodeData;
return true;
}
}
else { //catch all case. If something goes wrong, return false.
return false;
}
return true;
}
bool BinTree::insert(NodeData* nodeData) {
if (root != nullptr) { //if the root of the tree is not NULL, meaning that the BST object exists, we call our insertHelp function.
return insertHelp(this->root, nodeData); // <<<<<<<<<<<<<<< use the return value of the helper function here!!!
}
else { //If there is no root node created in our tree, we create a root node.
root = new Node;
root->left = nullptr;
root->right = nullptr;
root->data = nodeData;
return true;
}
return true;
}
void BinTree::makeEmpty() {
//You make a tree empty by deleting all notes in a post-order traversal.
postOrderDeleteNode(this->root); //I will call my private postOrderDelete helper function. I broke things up this way to make the code cleaner.
this->root = nullptr; // needs to be done just once
}
// ---------------------------------postOrderDeleteNode--------------------------------------------------
// Description: This is the private function that performs a post-order traversal of the nodes in a BinTree
// and deletes each node by deallocating the memory assigned when each node is created.
// ---------------------------------------------------------------------------------------------------
void BinTree::postOrderDeleteNode(const Node* rootNode) {
if (rootNode == nullptr) { //Base case, we have an empty tree at this node
return;
}
else {
postOrderDeleteNode(rootNode->left); //First we delete the left side of the tree
postOrderDeleteNode(rootNode->right); //Then we delete the right side of the tree.
delete rootNode->data; // <<<<<<<<<<<<<<<<<<<<<<<< delete the data too!!!!
delete rootNode; //We finally delete the root of the entire BST.
// set root to null it in makeEmpty() instead
}
}

Related

List All Function still trying to retrieve a node I deleted from a binary search tree

I have these functions to remove a node from my binary search tree:
bool collection::removeFromTree(const char name[])
{
for (treeNode * curr = root; curr;)
{
int8_t result = strcmp(name, curr->item->getName());
if (result == 0)
{
deleteNode(curr);
return true;
}
else if (result < 0)
curr = curr->left;
else if (result > 0)
curr = curr->right;
}
return false;
}
void collection::deleteNode(treeNode *& goneNode)
{
//if it's a leaf
if (!goneNode->left && !goneNode->right)
{
delete goneNode; //node's destructor gets invoked
goneNode = nullptr;
}
//if it has right child
else if (!goneNode->left)
{
goneNode = goneNode->right;
}
//if it has left child
else if (!goneNode->right)
{
goneNode = goneNode->left;
}
//if it has both children
else
{
treeNode * prev = nullptr;
treeNode * curr = goneNode->right;
while (curr->left)
{
prev = curr;
curr = curr->left;
}
//prev points to the copy over data
delete goneNode->item;
if (!prev)
{
goneNode->item = curr->item;
goneNode->right = curr->right;
curr->item = nullptr;
}
else
{
goneNode->item = curr->item;
curr->item = nullptr;
prev->left = curr->right;
}
}
}
This runs fine, but when I try to list all the elements in my tree after deleting a node (with these functions):
void collection::displayByName() const
{
std::cout << std::endl
<< "========================================" << std::endl;
//display the tree inorder
listAll(root);
}
void collection::listAll(const treeNode * const & root) const
{
if (root)
{
std::cout << *(root->item) << std::endl
<< "========================================" << std::endl;
listAll(root->left);
listAll(root->right);
}
}
I receive this error:
And when I quit the program after deleting a node (invoking these destructors):
collection::~collection()
{
delete root;
}
collection::treeNode::~treeNode()
{
delete left;
delete right;
}
I recieve this error:
Any suggestions would be greatly appreciated because I see no reason for my listAll() function to be calling nodes that I've already deleted.
By the way, this is my struct for my treeNode:
struct treeNode
{
treeNode();
treeNode(vendor *& item);
~treeNode();
vendor * item;
treeNode *left, *right;
};
treeNode * root; //the bst
hashNode ** table; //the hash table
uint8_t capacity;
uint8_t size;
const static uint8_t INIT_CAP = 20;
When you need to remove a node from a singly linked list or a tree, I find using a pointer to pointer is handy. Namely, if we have a treeNode** ptr;, then *ptr is the pointer to our node. So, if ptr = &root, then *ptr = nullptr sets root to nullptr.
I removed the deleteNode function and threw its logic in the removeFromTree function.
bool collection::removeFromTree(const char name[])
{
treeNode** ptr = &root;
Instead of being a pointer to treeNode, ptr will point to a treeNode* inside the tree structure. This way, we can modify the pointer that led us to the current node. The lines marked //same as before have the same logic you were using, just possibly modified to account for the fact ptr has another level of dereferencing to do.
int result; //same as before
while (*ptr) //While we haven't hit a dead end
{
result = strcmp(name, (*ptr)->item->getName()); //same as before
if (result < 0) //same as before
ptr = &((*ptr)->left); //same as before
else if (result > 0) //same as before
ptr = &((*ptr)->right); //same as before
else //begin deleteNode() logic
{
if ((*ptr)->left && (*ptr)->right) //two children
{
Here, we use pointers to member because the alternative was a conditional operator on every line. If a node has two children, we need to find either the rightmost node on the left side, or the leftmost node on the right side. That's the node we can replace the current node with.
treeNode* treeNode::*dir = some_condition ? &treeNode::right : &treeNode::left; //pointer to treeNode member of type treeNode*
treeNode* treeNode::*ndir = some_condition ? &treeNode::left : &treeNode::right; //pointer to treeNode member of type treeNode*
dir now either points to left or right, which is the direction we are searching the tree for. ndir is the opposite direction. So, if we want the rightmost node on the left side, (*ptr)->*dir == (*ptr)->left and (*ptr->*ndir == (*ptr)->right. If we want the leftmost right node, it would be reversed. This is just a more complicated way to do less work, really. It shouldn't be hard to remove. some_condition is just either true or false. true means the left side of the tree (from the current node) loses a node, and false means the right side does.
treeNode** replacement = &((*ptr)->*ndir); //the node to replace the current one with
while ((*replacement)->*dir) //While we aren't at the edge
replacement = &((*replacement)->*dir);
This loops until *replacement is the node we need to replace *ptr with.
treeNode* rep_branch = (*replacement)->*ndir; //If the replacement node had a child, this is now it
(*replacement)->left = (*ptr)->left; //Copy current to replacement
(*replacement)->right = (*ptr)->right; //Copy current to replacement
(*ptr)->left = nullptr; //null out current in case of destructor
(*ptr)->right = nullptr; //null out current in case of destructor
Now, the replacement node is pointing to the node-to-be-deleted's children, and our soon to be expired node has no children anymore. Now, it's safe to delete the unwanted node. If the node class had a destructor to delete its children, the left and right pointers were set to nullptr just in case.
delete *ptr; //delete unwanted node
*ptr = *replacement; //replacement node has taken the unwanted node's place in the tree
*replacement = rep_branch; //The replacement's child takes its own place
}
This completes the tree's structure. Wherever the unwanted node was, the replacement node has taken its place. And because the replacement node was required to be an edge node, it had at most one child. We just replace it with the child.
else if ((*ptr)->left) //one child on left
{
treeNode* current = *ptr;
*ptr = (*ptr)->left; //replace current with left
current->left = nullptr; //null out for safety
delete current;
}
else if ((*ptr)->right) //one child on right
{
treeNode* current = *ptr;
*ptr = (*ptr)->right; //replace current with right
current->right = nullptr; //null out for safety
delete current;
}
else //no children
{
delete *ptr;
*ptr = nullptr;
}
return true; //yay it's over
}
}
return false; //never found it
}
The rest is fairly straightforward, just replacing easier nodes and returning. Hopefully this gives you some ideas about how to approach problems like this, and the occasional uses of some of these structures. This is what I meant about using treeNode** over treeNode* for operations like this.

unsorted linked list implementation check full

I am working on unsorted linked list check full currently, below is my specification and implementation.
Specification:
#ifndef UNSORTEDLIST_H
#define UNSORTEDLIST_H
#include <iostream>
using namespace std;
struct Node {
float element;
Node* next;
};
class UnsortedList
{
public:
UnsortedList();
bool IsEmpty();
bool IsFull();
void ResetList();
void MakeEmpty();
int LengthIs();
bool IsInTheList(float item);
void InsertItem(float item);
void DeleteItem(float item);
float GetNextItem();
private:
Node* data;
Node* currentPos;
int length;
};
#endif
And implemetation:
UnsortedList::UnsortedList()
{
length = 0;
data = NULL;
currentPos = NULL;
}
bool UnsortedList:: IsEmpty(){
if(length == 0)
{
return true;
}
else
{
return false;
}
}
bool UnsortedList::IsFull(){
Node* ptr = new Node();
if(ptr == NULL)
return true;
else
{
delete ptr;
return false;
}
}
void UnsortedList::ResetList(){
currentPos = NULL;
}
void UnsortedList::MakeEmpty()
{
Node* tempPtr = new Node();
while(data != NULL)
{
tempPtr = data;
data = data->next;
delete tempPtr;
}
length = 0;
}
int UnsortedList::LengthIs(){
return length;
}
bool UnsortedList:: IsInTheList(float item){
Node* location = new Node();
location = data;
bool found = false;
while(location != NULL && !found)
{
if(item == location->element)
found = true;
else
location = location->next;
}
return found;
}
void UnsortedList:: InsertItem(float item){
Node* location = new Node();
location->element = item;
location->next=data;
data = location;
length++;
}
void UnsortedList:: DeleteItem(float item){
Node* location = data;
Node* tempPtr;
if(item == data->element){
tempPtr = location;
data = data->next;
}
else{
while(!(item == (location->next) ->element) )
location = location->next;
tempPtr = location->next;
location->next = (location->next)->next;
}
delete tempPtr;
length--;
}
float UnsortedList::GetNextItem(){
if(currentPos == NULL)
currentPos = data;
else
currentPos = currentPos->next;
return currentPos->element;
}
1.In the constructor, why don't assign currentPos as null?
2.In the IsInTheList function, Why points to pointer "next" ? Isn't next is a null pointer since it has been declared in struct as Node* next?
The pointer value is not set to NULL value by default, you should set to to null explicitly. Also instead of using NULL, choose using nullptr.
This code is rather incomplete, so it is difficult to answer your questions.
This does not contain the code to insert an item in the list, which is where I would expect both the next and currentPos pointers to be set. However, that's based on a number of assumptions.
However, I don't see where next is used in the "check full function" at all, so that question is a bit confusing.
I'll also point out that this code has a glaring memory leak. The first line in IsInTheList allocates memory for a new Node, which is immediately lost with location = data.
Pointers (like any other basic type) need to be initialized before use. A value of NULL is still a value.
The code you provided seems to be very incomplete. Is data supposed to be the head of your list? I am not sure how you define "fullness". If you want to test if the list is empty, you can see if your "head" of the list is null:
bool UnsortedList::IsEmpty() {
if (data == NULL) {return true;} // if there is no first element, empty
else {return false;} // if there is ANY element, not empty
}
Or more compactly:
bool UnsortedList::Empty() {
return (data == NULL);
}
When a node is added to a linked list, we usually add the node as a whole and modify the element that came before it. For example, we might create a new node and add it using code like the following:
// implementation file
void UnsortedList::InsertItem(const float& item) {
if (data == NULL) { // no elements in list, so new node becomes the head
data = new Node; // allocate memory for new node
data->element = item; // fill with requested data
data->next = NULL; // there is no element after the tail
}
else {
new_node = new Node; // allocate memory
new_node->element = item // set data
new_node->next = NULL; // new end of the list, so it points to nothing
tail->next = new_node; // have the OLD end node point to the NEW end
tail = new_node; // have the tail member variable move up
}
}
// driver file
int main() {
UnsortedList my_list;
float pie = 3.14159;
my_list.AddNode(pie);
return 0;
}
Please note that I made use of a Node* member variable called tail. It is a good idea to keep track of both where the list begins and ends.
In your IsFull function, it will always return false since it can always create a new Node*. Except perhaps if you run out of memory, which is probably more problematic.
Your functions are rather confusing and your pointer work leaves many memory leaks. You might want to review the STL list object design here.

Linked List Seg Fault when Returning Component

I am working on a program for my c++ class that is basically a small book store application. The book store is to have a linked list of Members(ListMemberType) and a linked list of Books(ListBookType). Each node of the linked list is made up of a link to the next component and a component. The component is of type BookType.
Here is the header file for ListBookType
#include "bookType.h"
typedef BookType ItemType; // Type of each component
struct NodeType; // Forward declaration
class ListBookType {
public:
const ListBookType& operator=(const ListBookType& rightObject);
//overload assignment operator
//will set the left side equal to right side
void Replace(ItemType theNewItem, ItemType theOldItem);
//Pre:These are the same book(ISBN is the same)
//Post: old component will be replaced with the new component
ListBookType();
// Constructor
// Post: Empty list has been created
~ListBookType();
// Destructor
// Post: All nodes are returned to the heap
ListBookType(const ListBookType& otherList);
// Copy constructor
// Post: A deep copy of otherList is created and dataPtr is the
// external pointer to this copy
// Action respnsibilities
void Insert(ItemType item);
// Pre: List is not full and item is not in the list
// Post: item is in the list and length has been incremented
void Delete(ItemType item);
// Post: item is not in the list
void ResetList();
// The current position is reset to access the first item in the list
ItemType GetNextItem();
// Assumptions: No transformers are called during the iteration.
// There is an item to be returned; that is, HasNext is true when
// this method is invoked
// Pre: ResetList has been called if this is not the first iteration
// Post: Returns item at the current position.
// Knowledge responsibility
int GetLength() const;
// Post: Returns the length of the list
bool IsEmpty() const;
// Post: Returns true if list is empty; false otherwise
bool IsFull() const;
// Post: Returns true if list if full; false otherwise
bool IsThere (ItemType item ) const;
// Post: Returns true if item is in the list and false otherwise
bool HasNext() const;
// Returns true if there is another item to be returned; false
// otherwise
ItemType GetBook(ItemType bookToGet)const;
//Pre: Book is in list
//Post: the book is returned
//Pre: Book is in the list
//Post: Book with matching ISBn will be returned
private:
NodeType* dataPtr; // Pointer to the first node in the list
int length;
NodeType* currentPos; // Pointer to the current position in a traversal
NodeType* lastPtr;
};
And here is the part of the Specification file that I know contains my error, along the parts that I believe may be causing the error.
#include "listBookType.h"
#include "bookType.h"
#include <iostream>
#include <cstddef> // For NULL
using namespace std;
typedef NodeType* NodePtr;
struct NodeType {
ItemType component;
NodePtr link;
};
const ListBookType& ListBookType::operator=(const ListBookType& rightObject) {
cout<<"Assignment operator bookList"<<endl;
NodePtr fromPtr; // Pointer into list being copied from
NodePtr toPtr; // Pointer into new list being built
if(this != &rightObject) {
if (rightObject.dataPtr == NULL) {
dataPtr = NULL;
return *this;
}
// Copy first node
fromPtr = rightObject.dataPtr;
dataPtr = new NodeType;
dataPtr->component = fromPtr->component;
// Copy remaining nodes
toPtr = dataPtr;
fromPtr = fromPtr->link;
while (fromPtr != NULL)
// Copying nodes from original to duplicate
{
toPtr->link = new NodeType; // Store new node in link of last
// node added to new list
toPtr = toPtr->link; // toPtr points to new node
toPtr->component = fromPtr->component; // Copy component to new node
fromPtr = fromPtr->link; // fromPtr points to next node
// of original list
}
toPtr->link = NULL;
lastPtr = toPtr; // Set last pointer
}
return *this;
}
ItemType ListBookType::GetBook(ItemType bookToGet)const {
NodePtr currPtr = dataPtr; // Loop control pointer
NodePtr tempPtr = NULL;
while (currPtr != NULL && currPtr->component != bookToGet
&& currPtr->component < bookToGet) {
tempPtr = currPtr;
currPtr = currPtr->link;
}
cout<<"right before return of getBook"<< endl;
return tempPtr->component;
}
ListBookType::ListBookType(const ListBookType& otherList) {
cout<<"copy construct book list"<< endl;
NodePtr fromPtr; // Pointer into list being copied from
NodePtr toPtr; // Pointer into new list being built
if (otherList.dataPtr == NULL) {
dataPtr = NULL;
return;
}
// Copy first node
fromPtr = otherList.dataPtr;
dataPtr = new NodeType;
dataPtr->component = fromPtr->component;
// Copy remaining nodes
toPtr = dataPtr;
fromPtr = fromPtr->link;
while (fromPtr != NULL) { // Copying nodes from original to duplicate
toPtr->link = new NodeType; // Store new node in link of last
// node added to new list
toPtr = toPtr->link; // toPtr points to new node
toPtr->component = fromPtr->component; // Copy component to new node
fromPtr = fromPtr->link; // fromPtr points to next node
// of original list
}
toPtr->link = NULL;
lastPtr = toPtr; // Set last pointer
}
ListBookType::~ListBookType() {
cout<< "destructor book list"<< endl;
NodePtr tempPtr;
NodePtr currPtr = dataPtr;
while (currPtr != NULL) {
tempPtr = currPtr;
currPtr = currPtr->link;
delete tempPtr;
}
}
The issue I am having is in GetBook(ItemType bookToGet). I tracked the problem down and I am seg faulting when I return tempPtr->component. I am having this same issue in a few other places in the code and I don't know why I am seg faulting here or what the underlying issue may be. (Note: BookType class does NOT contain any dynamic data that would need an the assignment operator overloaded or a copy constructor)
I would really appreciate any help at all. I feel like I am just missing something important that I don't know about.
In your GetBook routine consider the case when dataPtr is already NULL.
You would be accessing return tempPtr->component; //causing a segfault.
You should handle the case when dataPtr is NULL separately.
while (currPtr != NULL && currPtr->component != bookToGet // dataPtr = currPtr is NULL
&& currPtr->component < bookToGet){
tempPtr = currPtr;
currPtr = currPtr->link;
}
cout<<"right before return of getBook"<< endl;
return tempPtr->component; // SEGFAULT!
If the condition in the while statement is false the first time it is evaluated, then tempPtr will never be set to anything other than NULL, and the tempPtr->component in the return statement tries to dereference a null pointer and crashes.
Think about what would be a reasonable default for your function to return if the while condition is false the first time, and then if tempPtr is NULL just before the return statement, return that default instead.

C++ Linked List assignment: trouble with insertion and deletion

I am working on a linked list implementation in C++. I am making progress but am having trouble getting the insertion functionality and deletion functionality to work correctly. Below is list object in the C++ header file:
#ifndef linkList_H
#define linkList_h
//
// Create an object to represent a Node in the linked list object
// (For now, the objects to be put in the list will be integers)
//
struct Node
{
Node() : sentinel(0) {}
int number;
Node* next;
Node* prev;
Node* sentinel;
};
//
// Create an object to keep track of all parts in the list
//
class List
{
public:
//
// Contstructor intializes all member data
//
List() : m_listSize(0), m_listHead(0) {}
//
// methods to return size of list and list head
//
Node* getListHead() const { return m_listHead; }
unsigned getListSize() const { return m_listSize; }
//
// method for adding and inserting a new node to the linked list,
// retrieving and deleting a specified node in the list
//
void addNode(int num);
void insertNode(Node* current);
void deleteNode(Node* current);
Node* retrieveNode(unsigned position);
private:
//
// member data consists of an unsigned integer representing
// the list size and a pointer to a Node object representing head
//
Node* m_listHead;
unsigned m_listSize;
};
#endif
And here is the implementation (.cpp) file:
#include "linkList.h"
#include <iostream>
using namespace std;
//
// Adds a new node to the linked list
//
void List::addNode(int num)
{
Node *newNode = new Node;
newNode->number = num;
newNode->next = m_listHead;
m_listHead = newNode;
++m_listSize;
}
//
// NOTWORKING: Inserts a node which has already been set to front
// of the list
//
void List::insertNode(Node* current)
{
// check to see if current node already at
// head of list
if(current == m_listHead)
return;
current->next = m_listHead;
if(m_listHead != 0)
m_listHead->prev = current;
m_listHead = current;
current->prev = 0;
}
//
// NOTWORKING: Deletes a node from a specified position in linked list
//
void List::deleteNode(Node* current)
{
current->prev->next = current->next;
current->next->prev = current->prev;
}
//
// Retrieves a specified node from the list
//
Node* List::retrieveNode(unsigned position)
{
if(position > (m_listSize-1) || position < 0)
{
cout << "Can't access node; out of list bounds";
cout << endl;
cout << endl;
exit(EXIT_FAILURE);
}
Node* current = m_listHead;
unsigned pos = 0;
while(current != 0 && pos != position)
{
current = current->next;
++pos;
}
return current;
}
After running a brief test program in the client C++ code, here is the resulting output:
Number of nodes: 10
Elements in each node:
9 8 7 6 5 4 3 2 1 0
Insertion of node 5 at the list head:
4 9 8 7 6 5 4 9 8 7
Deletion of node 5 from the linked list
As you can see, the insertion is not simply moving node 5 to head of list, but is overwriting other nodes beginning at the third position. The pseudo code I tried to implement came from the MIT algorithms book:
LIST-INSERT(L, x)
next[x] <- head[L]
if head[L] != NIL
then prev[head[L]] <- x
head[L] <- x
prev[x] <- NIL
Also the deletion implementation is just crashing when the method is called. Not sure why; but here is the corresponding pseudo-code:
LIST-DELET'
next[prev[x]] <- next[x]
prev[next[x]] <- prev[x]
To be honest, I am not sure how the previous, next and sentinel pointers are actually working in memory. I know what they should be doing in a practical sense, but looking at the debugger it appears these pointers are not pointing to anything in the case of deletion:
(*current).prev 0xcdcdcdcd {number=??? next=??? prev=??? ...} Node *
number CXX0030: Error: expression cannot be evaluated
next CXX0030: Error: expression cannot be evaluated
prev CXX0030: Error: expression cannot be evaluated
sentinel CXX0030: Error: expression cannot be evaluated
Any help would be greatly appreciated!!
You have got an error in addNode(). Until you fix that, you can't expect insertNode to work.
Also, I think your design is quite silly. For example a method named "insertNode" should insert a new item at arbitrary position, but your method insertNode does a pretty different thing, so you should rename it. Also addNode should be renamed. Also as glowcoder wrote, why are there so many sentinels? I am affraid your class design is bad as a whole.
The actual error is that you forgot to set prev attribute of the old head. It should point to the new head.
void List::addNode(int num)
{
Node *newNode = new Node;
newNode->number = num;
newNode->next = m_listHead;
if(m_listHead) m_listHead->prev = newNode;
m_listHead = newNode;
++m_listSize;
}
Similarly, you have got another error in deleteNode(). It doesn't work when deleting last item from list.
void List::deleteNode(Node* current)
{
m_listSize--;
if(current == m_listHead) m_listHead = current->next;
if(current->prev) current->prev->next = current->next;
if(current->next) current->next->prev = current->prev;
}
Now you can fix your so-called insertNode:
void List::insertNode(Node* current)
{
int value = current->number;
deleteNode(current);
addNode(value);
}
Please note that I wrote everything here without compiling and testing in C++ compiler. Maybe there are some bugs, but still I hope it helps you at least a little bit.
In deleteNode, you are not handling the cases where current->next and/or current->prev is null. Also, you are not updating the list head if current happens to be the head.
You should do something like this:
node* next=current->next;
node* prev=current->prev;
if (next!=null) next->prev=prev;
if (prev!=null) prev->next=next;
if (m_listhead==current) m_list_head=next;
(Warning: I have not actually tested the code above - but I think it illustrates my idea well enough)
I am not sure what exactly your InsertNode method does, so I can't offer any help there.
OK.
As #Al Kepp points out, your "add node" is buggy. Look at Al's code and fix that.
The "insert" that you are doing does not appear to be a normal list insert. Rather it seems to be a "move to the front" operation.
Notwithstanding that, you need to delete the node from its current place in the list before you add it to the beginning of the list.
Update
I think you have misunderstood how insert should work. It should insert a new node, not one that is already in the list.
See below for a bare-bones example.
#include <iostream>
// List Node Object
//
struct Node
{
Node(int n=0);
int nData;
Node* pPrev;
Node* pNext;
};
Node::Node(int n)
: nData(n)
, pPrev(NULL)
, pNext(NULL)
{
}
//
// List object
//
class CList
{
public:
//
// Contstructor
//
CList();
//
// methods to inspect list
//
Node* Head() const;
unsigned Size() const;
Node* Get(unsigned nPos) const;
void Print(std::ostream &os=std::cout) const;
//
// methods to modify list
//
void Insert(int nData);
void Insert(Node *pNew);
void Delete(unsigned nPos);
void Delete(Node *pDel);
private:
//
// Internal data
//
Node* m_pHead;
unsigned m_nSize;
};
/////////////////////////////////////////////////////////////////////////////////
CList::CList()
: m_pHead(NULL)
, m_nSize(0)
{
}
Node *CList::Head() const
{
return m_pHead;
}
unsigned CList::Size() const
{
return m_nSize;
}
void CList::Insert(int nData)
{
Insert(new Node(nData));
}
void CList::Insert(Node *pNew)
{
pNew->pNext = m_pHead;
if (m_pHead)
m_pHead->pPrev = pNew;
pNew->pPrev = NULL;
m_pHead = pNew;
++m_nSize;
}
void CList::Delete(unsigned nPos)
{
Delete(Get(nPos));
}
void CList::Delete(Node *pDel)
{
if (pDel == m_pHead)
{
// delete first
m_pHead = pDel->pNext;
if (m_pHead)
m_pHead->pPrev = NULL;
}
else
{
// delete subsequent
pDel->pPrev->pNext = pDel->pNext;
if (pDel->pNext)
pDel->pNext->pPrev = pDel->pPrev;
}
delete pDel;
--m_nSize;
}
Node* CList::Get(unsigned nPos) const
{
unsigned nCount(0);
for (Node *p=m_pHead; p; p = p->pNext)
if (nCount++ == nPos)
return p;
throw std::out_of_range("No such node");
}
void CList::Print(std::ostream &os) const
{
const char szArrow[] = " --> ";
os << szArrow;
for (Node *p=m_pHead; p; p = p->pNext)
os << p->nData << szArrow;
os << "NIL\n";
}
int main()
{
CList l;
l.Print();
for (int i=0; i<10; i++)
l.Insert((i+1)*10);
l.Print();
l.Delete(3);
l.Delete(7);
l.Print();
try
{
l.Delete(33);
}
catch(std::exception &e)
{
std::cerr << "Failed to delete 33: " << e.what() << '\n';
}
l.Print();
return 0;
}

c++ binarysearchtree insert

I'm working on a project where I have to make a binary search tree that stores strings and takes account of doubles. While i've already tackled the specifics, I can't for the life of me get this damn insert function to work. It seems to only store the root node, leaving it's "children" NULL even though it does actually seem to assign the left and right pointers to new nodes. However when I attempt to output it, only the main parent (root) node exists. I guess the changes do not get saved for whatever reason.
Here's the header file:
#ifndef BST_H
#define BST_H
#include <string>
#include <vector>
#include <iostream>
using namespace std;
typedef string ItemType;
class Node
{
public:
Node(); // constructor
ItemType data; // contains item
Node* left; // points to left child
Node* right;// points to right child
int dataCount; // keeps track of repeats
vector<int> lineNumber; // keeps track of line numbers
};
class Tree
{
public:
Tree(); // constructor
// ~Tree(); // destructor. not working.
bool isEmpty(); // tests for empty tree
Node* find(Node* root, ItemType item); // finds an item
void insert(Node* root, ItemType item, int lineN, Tree tree); // inserts an item
void outputTree(Node* root); // lists all items in tree
void treeStats(Tree tree); // outputs tree stats
void clearTree(); // erases the tree (restart)
Node* getRoot(); // returns the root
void setRoot(Node*& root);
// void getHeight(Tree *root); // gets height of tree
private:
Node* root; // root of tree
int nodeCount; // number of nodes
};
#endif
cpp:
#include "BST.h"
bool setRootQ = true;
/** Node constructor- creates a node, sets children
* to NULL and ups the count. */
Node::Node()
{
left = right = NULL;
dataCount = 1;
}
/** Tree constructor- creates instance of tree
* and sets parameters to NULL */
Tree::Tree()
{
root = NULL;
nodeCount = 0;
}
/** Destructor- deallocates tree/node data;
* avoids heap leaks. SOMETHING WRONG. CAUSES SEGFAULT
Tree::~Tree()
{
if(this->root->left) // if a left child is present
{
delete this->root->left; //recursive call to destructor ("~Tree(->left)")
this->root->left = NULL;
}
if(this->root->right) // if a right child is present
{
delete this->root->right; //recursive call to destructor
this->root->right = NULL;
}
} */
/** Returns true if tree is empty.
* Otherwise returns false (DUH). */
bool Tree::isEmpty()
{
return root == NULL;
}
/** Searches tree for item; returns the node if found
* #param root- tree node.
* item- data to look for. */
Node* Tree::find(Node* root, ItemType item)
{
if(root == NULL) // if empty node
{
return NULL;
}
else if(item == root->data) // if found
{
return root;
}
else if(item < root->data) // if item is less than node
{
find(root->left, item);
}
else if(item > root->data) // if item is more than node
{
find(root->right, item);
}
return NULL;
}
/** Adds a new node to the tree. If duplicate, increases count.
* #param item- data to insert.
* root- tree node/ */
void Tree::insert(Node* root, ItemType item, int lineN, Tree tree)
{
Node* temp = find(tree.getRoot(), item);
if(temp != NULL) // if item already exists
{
temp->dataCount += 1;
temp->lineNumber.push_back(lineN);
return;
}
if(root == NULL) // if there is an empty space
{
root = new Node; // insert new node
root->data = item; // w/ data value
root->lineNumber.push_back(lineN);
nodeCount++;
if(setRootQ)
{
setRoot(root);
setRootQ = false;
}
return;
}
if(item < root->data)
{
insert(root->left, item, lineN, tree);
}
if(item > root->data)
{
insert(root->right, item, lineN, tree);
}
}
/** Outputs tree to console in inorder.
* #param root- tree root. */
void Tree::outputTree(Node* root)
{
if(isEmpty()) // if empty tree
{
cout << "Error: No items in tree" << endl; // error message
}
else
{
if(root->left != NULL)
{
outputTree(root->left);
}
cout << "- " << root->data << " (" << root->dataCount << ") line#s: ";
for(unsigned int i = 0; i < root->lineNumber.size(); i++)
{
cout << root->lineNumber[i] << ", ";
}
cout << endl;
if(root->right != NULL)
{
outputTree(root->right);
}
}
}
/** Displays tree stats including number of nodes,
* tree height, and more frequent item.
* #param tree- tree instance. */
void Tree::treeStats(Tree tree)
{
cout << "Number of entries: " << nodeCount << endl;
// unfinished
}
/** Clears tree.
void Tree::clearTree()
{
this->~Tree();
} */
/** Returns the root of the tree. */
Node* Tree::getRoot()
{
return root;
}
void Tree::setRoot(Node*& rootS)
{
root = rootS;
}
I realize my destructor isn't working but I'll tackle that myself later. I've been pulling my hair out over this trying to figure out what I'm missing, but to no avail. If anyone can give me any help and point me in the direction towards a solution I would greatly appreciate it.
i think it might have something to do with
void Tree::insert(Node* root, ItemType item, int lineN, Tree tree)
and instead should be something like
void Tree::insert(Node* &root, ItemType item, int lineN, Tree tree)
but when i try i get a "no matching function" error. :/
The solution you suggest yourself (with insert() taking Node *& root) will work. You have to make a corresponding change to the .h file, AND also change getRoot() to return Node *& in both .h and .cc files.
This will work. But your code has other problems. For example, setRoot and setRootQ don't do anything useful, as far as I can tell. The fourth argument to insert() only confuses things and does nothing to detect duplicates. It should be removed. To detect a duplicate simply do if (item == root->data) just before you do if (item < root->data) in the insert() method (i.e. it'll be even more similar to find()).
Also, if anyone besides you will ever use your code, you shouldn't require passing in getRoot() to methods like find() and insert(). Instead, create private helper methods, like find_helper(Node*, Item) and insert_helper(Node*&,Item), and have the public methods call them with the root node as the first argument. E.g.:
Node *find(Item item) { return find_helper(root, item); }
This would also make the weird return type of getRoot() unnecessary, and you could change it back to returning the plain Node*, or get rid of that accessor altogether.
Seems like there is a lot of pointer comparison occurring but the pointer member variables are not being initialized. Ensure that the pointer member variables are initialized properly so that they evaluate properly in if statements.
Change from:
class Node
{
public:
Node(); // constructor
...
};
to
class Node
{
public:
Node() : left(0), right(0) {}
...
};