C++ Vector in Heap Memory results in Segmentation Fault - c++

I am new to C++, and I'm sure this question must be easy to handle but I think I miss some main concepts. In the code, a Binary Tree is implemented, and we are asked to determine the number of nodes in that Binary Tree. As you can see in Node class, a Node has two member pointers: left that points to the Node at left, right that points to the Node at right. The input is the root node. Anyway, I found a solution like this:
Make a vector of Nodes, which I named nodes in the code, and append the root Node, and set the number of nodes (howManyNodes in the code) to 1.
While nodes is not empty (at the beginning we add the root node), check if it has a left and a right pointer. If they are available (i.e., not nullptr) , add those pointers to the vector of nodes (I use insert). At the same time, increase the number of nodes (howManyNodes in the code) by one.
After checking a if specific node has a left and right pointers, I remove that node from the list, for which I use pop_back() function.
At the end, the vector will be empty and I will obtain howManyNodes as the answer.
Here is the code, and I only implemented the count function. The rest is from the template.
#include <iostream>
#include <vector>
class Node {
public:
Node *left, *right;
Node() { left = right = nullptr; }
~Node() {
delete left;
left = nullptr;
delete right;
right = nullptr;
}
};
// I implement this part
int count(Node *n) {
int howManyNodes = 1;
std::vector<Node> *nodes = new std::vector<Node>;
nodes->push_back(*n);
while (!nodes->empty()){
Node* trial = new Node(nodes->back());
if (trial->left){
std::cout << trial->left << std::endl;
nodes->insert(nodes->begin(), *trial->left);
howManyNodes += 1;
}
if (trial->right){
std::cout << trial->right << std::endl;
nodes->insert(nodes->begin(), *trial->right);
howManyNodes += 1;
}
nodes->pop_back();
}
return howManyNodes;
}
// I implement this part.
int main() {
Node *n = new Node();
n->left = new Node();
n->right = new Node();
n->right->left = new Node();
n->right->right = new Node();
n->right->right->right = new Node();
// This should print a count of six nodes
std::cout << count(n) << std::endl;
// Deleting n is sufficient to delete the entire tree
// because this will trigger the recursively-defined
// destructor of the Node class.
delete n;
n = nullptr;
return 0;
}
The problem is, I can never get rid of segmentation fault. As I searched, it happens when the code is trying to access a memory it is not supposed to. My code might be easy to fix, but I have the following questions:
If std::vector uses heap memory for its members, why do I need to define a new vector here? In the main function (which I didn't write) everything is written by new, then I assumed I should also use new whenever possible but I don't understand the logic behind.
In my code, I want to use references, because I want to access only the pointers of Nodes and not modifying them - I learned that using the object itself requires making copies and slows down the process, so not preferable -. What part of my code is trying to modify any pointers?
Now that I defined a new vector, should I also delete it and make it equal to nullptr, before returning the value?
Thanks in advance!

The problem with your Node class is that it doesn't follow the rule of 3/5/0 and whenever you make a copy of a Node, and you are making a lot of copies, you have a trouble, because the left right nodes will be deleted once the copied object goes out of scope, and then again when you call delete yourself.
Short summary of bugs in your count implementation:
int count(Node *n) {
int howManyNodes = 1;
// doesn't make sense to allocate vector dynamically
// also you forgot to delete it
std::vector<Node> *nodes = new std::vector<Node>;
// keeping nodes as value will copy them, which leeds to double free
nodes->push_back(*n);
while (!nodes->empty()){
// another copy - thus another problem, but this time "only" a memory leak since you don't delete it
Node* trial = new Node(nodes->back());
if (trial->left){
std::cout << trial->left << std::endl;
// another copy
nodes->insert(nodes->begin(), *trial->left);
howManyNodes += 1;
}
if (trial->right){
std::cout << trial->right << std::endl;
// another copy
nodes->insert(nodes->begin(), *trial->right);
howManyNodes += 1;
}
nodes->pop_back();
}
return howManyNodes;
}
How it might be implemented without copying any objects:
void count(Node* n, int& numNodes)
{
numNodes++;
Node* left = n->left;
Node* right = n->right;
if (left) count(left, numNodes);
if (right) count(right, numNodes);
}
int main() {
Node *n = new Node();
n->left = new Node();
n->right = new Node();
n->right->left = new Node();
n->right->right = new Node();
n->right->right->right = new Node();
int numNodes{0};
count(n, numNodes);
std::cout << numNodes << std::endl;
delete n;
return 0;
}
It is a recursive approach, so maybe not the best. If you really need to implement some kind of a tree by hand, use std::unique_ptr, delete explicitly the copy constructor/assignment, and implement the method count inside your Tree class, if you plan to have sth like that.

As #pptaszni explained, in my code I was making copies of the instances and that was causing problems. The recursive approach suggested by #pptaszni is much easier and preferable, which I couldn't think of before. I also corrected my approach by passing pointers instead of values. This works:
#include <iostream>
#include <vector>
class Node {
public:
Node *left, *right;
Node() { left = right = nullptr; }
~Node() {
delete left;
left = nullptr;
delete right;
right = nullptr;
}
};
int count(Node *n) {
int howManyNodes = 1;
std::vector<Node*> nodes = {};
nodes.push_back(n);
while (!nodes.empty()){
Node* trial = nodes.back();
if (trial->left){
nodes.insert(nodes.begin(), trial->left);
howManyNodes += 1;
}
if (trial->right){
nodes.insert(nodes.begin(), trial->right);
howManyNodes += 1;
}
nodes.pop_back();
}
// Implement count() here.
return howManyNodes;
}
int main() {
Node *n = new Node();
n->left = new Node();
n->right = new Node();
n->right->left = new Node();
n->right->right = new Node();
n->right->right->right = new Node();
// This should print a count of six nodes
std::cout << count(n) << std::endl;
// Deleting n is sufficient to delete the entire tree
// because this will trigger the recursively-defined
// destructor of the Node class.
delete n;
n = nullptr;
return 0;
}

Related

Linked List: How to implement Destructor, Copy Constructor, and Copy Assignment Operator? [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 9 months ago.
Improve this question
This is my C++ code:
#include <iostream>
using namespace std;
typedef struct Node
{
int data;
Node* next;
}Node;
class LinkedList
{
private:
Node* first;
Node* last;
public:
LinkedList() {first = last = NULL;};
LinkedList(int A[], int num);
~LinkedList();
void Display();
void Merge(LinkedList& b);
};
// Create Linked List using Array
LinkedList::LinkedList(int A[], int n)
{
Node* t = new Node;
if (t == NULL)
{
cout << "Failed allocating memory!" << endl;
exit(1);
}
t->data = A[0];
t->next = NULL;
first = last = t;
for (int i = 1; i < n; i++)
{
t = new Node;
if (t == NULL)
{
cout << "Failed allocating memory!" << endl;
exit(1);
}
t->data = A[i];
t->next = NULL;
last->next = t;
last = t;
}
}
// Deleting all Node in Linked List
LinkedList::~LinkedList()
{
Node* p = first;
Node* tmp;
while (p != NULL)
{
tmp = p;
p = p->next;
delete tmp;
}
}
// Displaying Linked List
void LinkedList::Display()
{
Node* tmp;
for (tmp = first; tmp != NULL; tmp = tmp->next)
cout << tmp->data << " ";
cout << endl;
}
// Merge two linked list
void LinkedList::Merge(LinkedList& b)
{
// Store first pointer of Second Linked List
Node* second = b.first;
Node* third = NULL, *tmp = NULL;
// We find first Node outside loop, smaller number, so Third pointer will store the first Node
// Then, we can only use tmp pointer for repeating process inside While loop
if (first->data < second->data)
{
third = tmp = first;
first = first->next;
tmp->next = NULL;
}
else
{
third = tmp = second;
second = second->next;
tmp->next = NULL;
}
// Use while loop for repeating process until First or Second hit NULL
while (first != NULL && second != NULL)
{
// If first Node data is smaller than second Node data
if (first->data < second->data)
{
tmp->next = first;
tmp = first;
first = first->next;
tmp->next = NULL;
}
// If first Node data is greater than second Node data
else
{
tmp->next = second;
tmp = second;
second = second->next;
tmp->next = NULL;
}
}
// Handle remaining Node that hasn't pointed by Last after while loop
if (first != NULL)
tmp->next = first;
else
tmp->next = second;
// Change first to what Third pointing at, which is First Node
first = third;
// Change last pointer from old first linked list to new last Node, after Merge
Node* p = first;
while (p->next != NULL)
{
p = p->next;
}
last = p;
// Destroy second linked list because every Node it's now connect with first linked list
// This also prevent from Double free()
b.last = NULL;
b.first = NULL;
}
int main()
{
int arr1[] = {4, 8, 12, 14, 15, 20, 26, 28, 30};
int arr2[] = {2, 6, 10, 16, 18, 22, 24};
int size1 = sizeof(arr1) / sizeof(arr1[0]);
int size2 = sizeof(arr2) / sizeof(arr2[0]);
LinkedList l1(arr1, size1);
LinkedList l2(arr2, size2);
l1.Display();
l2.Display();
// Merge two linked list, pass l2 as reference
l1.Merge(l2);
l1.Display();
return 0;
}
I'm beginner on C++ and in this code, I practice how to Merge two linked list. This actually works perfectly. I've successfully Merge the two Linked List in sorted order.
But, there's people said that I should've follow the Rule of Three on C++. Which implement: Destructor, Copy Constructor, and Copy Assignment Operator.
I've watched many videos about that. I do understand that is basically handle Shallow Copy especially when we don't want two different object point to the same address of memory. But, for my problem is, I still don't know how to Implement it on a Class that working on a Linked List just like my code above.
Someone said, in my main(), this code: l1.Merge(l2); is somehow incorrect because I don't have explicit Copy Constructor.
And if you look at my Merge() function, in Last line, if I didn't to this: b.last = NULL; and b.first = NULL; , which simply destroy pointer of Second Linked list, the Compiler give me warning: Double free() detected.
So, I think my question is:
How can this code: l1.Merge(l2); is have something to do with Copy Constructor?
Is Double free() happened because I don't implement the Rule of Three? If yes, how to address them?
How to write the Rule of Three based on my code? When or How to use them?
Based on this Code, is there something wrong? Do I still need the Rule of Three if my Program only want to Merge Linked List?
Thank You. I hope someone can explain to me like I'm 10 years old. and hope someone can write me some Code.
But, for my problem is, I still don't know how to Implement [Rule of Three] on a Class that working on a Linked List just like my code above.
You simply implement the copy constructor and copy assignment operator to iterate the input list, making a copy of each node and inserting them into your target list. You already have a working destructor. In the case of the copy assignment operator, you can usually use the copy-swap idiom to implement it using the copy constructor to avoid repeating yourself.
Someone said, in my main(), this code: l1.Merge(l2); is somehow incorrect because I don't have explicit Copy Constructor.
Then you were told wrong. Your Merge() code has nothing to do with a copy constructor.
And if you look at my Merge() function, in Last line, if I didn't to this: b.last = NULL; and b.first = NULL;, which simply destroy pointer of Second Linked list, the Compiler give me warning: Double free() detected.
Correct. Since you are moving the nodes from the input list to the target list, you need to reset the input list so it doesn't point at the moved nodes anymore. Otherwise, the destructor of the input list will try to free them, as will the destructor of the target list.
How can this code: l1.Merge(l2); is have something to do with Copy Constructor?
It doesn't have anything to do with it.
Is Double free() happened because I don't implement the Rule of Three?
Not in your particular example, as you are not performing any copy operations. But, in general, not implementing the Rule of Three can lead to double frees, yes.
How to write the Rule of Three based on my code?
See the code below.
Do I still need the Rule of Three if my Program only want to Merge Linked List?
No. Only when you want to make copies of lists.
With that said, here is an implementation that includes the Rule of Three:
#include <iostream>
#include <utility>
struct Node
{
int data;
Node *next;
};
class LinkedList
{
private:
Node *first;
Node *last;
public:
LinkedList();
LinkedList(const LinkedList &src);
LinkedList(int A[], int num);
~LinkedList();
LinkedList& operator=(const LinkedList &rhs);
void Display() const;
void Merge(LinkedList &b);
};
// Create Linked List using default values
LinkedList::LinkedList()
: first(NULL), last(NULL)
{
}
// Create Linked List using Array
LinkedList::LinkedList(int A[], int n)
: first(NULL), last(NULL)
{
Node **p = &first;
for (int i = 0; i < n; ++i)
{
Node *t = new Node;
t->data = A[i];
t->next = NULL;
*p = t;
p = &(t->next);
last = t;
}
}
// Create Linked List by copying another Linked List
LinkedList::LinkedList(const LinkedList &src)
: first(NULL), last(NULL)
{
Node **p = &first;
for (Node *tmp = src.first; tmp; tmp = tmp->next)
{
Node* t = new Node;
t->data = tmp->data;
t->next = NULL;
*p = t;
p = &(t->next);
last = t;
}
}
// Deleting all Node in Linked List
LinkedList::~LinkedList()
{
Node *p = first;
while (p)
{
Node *tmp = p;
p = p->next;
delete tmp;
}
}
// Update Linked List by copying another Linked List
LinkedList& LinkedList::operator=(const LinkedList &rhs)
{
if (&rhs != this)
{
LinkedList tmp(rhs);
std::swap(tmp.first, first);
std::swap(tmp.last, last);
}
return *this;
}
// Displaying Linked List
void LinkedList::Display() const
{
for (Node *tmp = first; tmp; tmp = tmp->next)
std::cout << tmp->data << " ";
std::cout << std::endl;
}
// Merge two linked list
void LinkedList::Merge(LinkedList& b)
{
if ((&b == this) || (!b.first))
return;
if (!first)
{
first = b.first; b.first = NULL;
last = b.last; b.last = NULL;
return;
}
// Store first pointer of Second Linked List
Node *second = b.first;
Node *third, **tmp = &third;
// We find first Node outside loop, smaller number, so Third pointer will store the first Node
// Then, we can only use tmp pointer for repeating process inside While loop
// Use while loop for repeating process until First or Second hit NULL
do
{
// If first Node data is smaller than second Node data
if (first->data < second->data)
{
*tmp = first;
tmp = &(first->next);
first = first->next;
}
// If first Node data is greater than second Node data
else
{
*tmp = second;
tmp = &(second->next);
second = second->next;
}
*tmp = NULL;
}
while (first && second);
// Handle remaining Node that hasn't pointed by Last after while loop
*tmp = (first) ? first : second;
// Change first to what Third pointing at, which is First Node
first = third;
// Change last pointer from old first linked list to new last Node, after Merge
Node *p = first;
while (p->next)
{
p = p->next;
}
last = p;
// Destroy second linked list because every Node it's now connect with first linked list
// This also prevent from Double free()
b.first = b.last = NULL;
}
int main()
{
int arr1[] = {4, 8, 12, 14, 15, 20, 26, 28, 30};
int arr2[] = {2, 6, 10, 16, 18, 22, 24};
int size1 = sizeof(arr1) / sizeof(arr1[0]);
int size2 = sizeof(arr2) / sizeof(arr2[0]);
LinkedList l1(arr1, size1);
LinkedList l2(arr2, size2);
LinkedList l3(l1);
LinkedList l4;
l1.Display();
l2.Display();
l3.Display();
l4.Display();
// Merge two linked list, pass l2 as reference
l3.Merge(l2);
l4 = l3;
l1.Display();
l2.Display();
l3.Display();
l4.Display();
return 0;
}
Demo
There are several questionable practices applied in this code, and there is also a bug.
First, the bug. When you create a list, it news all its nodes and keeps track of them using pointers. When you assign a list to another, you literally copy the pointer values. Not only have you now lost the nodes of the assigned list (because you overwrote them) and got a memory leak (because now there's no pointer pointing to the allocated nodes), you also now have the same pointers on two different lists, pointing to the same nodes. When the lists are destroyed, both of them try to delete their nodes, and you end up freeing the same memory twice. Yuk.
The solution to this bug is to implement the assignment operator.
Then, the questionable practices:
using namespace std; (Why is "using namespace std;" considered bad practice?)
You're assigning the members of LinkedList in the constructor body, instead of passing the values directly to their constructor in the initialization list. (What is this weird colon-member (" : ") syntax in the constructor?)
Declaring an array parameter (int[]) is declaring a pointer. Just be aware of it.
new cannot return NULL! It's useless to check its return value. If it can't allocate, it will simply throw an exception.
NULL is the inappropriate constant to use. You can use nullptr, it's the C++ equivalent of NULL, except it's type safe.
Manual memory management with new and delete is tricky to get right (as you figured out yourself). You might be interested in using std::unique_ptr or std::shared_ptr to alleviate the burden. They would have caught the bug.
Now, please: do not write in C++ like it's C with classes. I understand that you may not have encountered all of the features I presented here, but anyway now you know about them :)

Linked list gets corrupted when I instantiate a Node immediately after calling append

I would like to know what may I be misunderstanding in the code below (the commented out lines). If I try to create the Node before going through the linked list, the values from root->next get messed up, from the second iteration onward.
The way the code is right now works, but I don't see why the other way doesn't.
template <class T>
class Node
{
public:
T data;
Node* next;
Node(T nodeData) : data(nodeData) { next = nullptr; }
void append(T nodeData)
{
// If I uncomment this, I get the problem
//Node newNode (nodeData);
Node* insertionNode = this;
while(insertionNode->next != nullptr)
{
insertionNode = insertionNode->next;
}
// Instead of using newNode, I must create the node here
// insertionNode->next = &newNode;
insertionNode->next = new Node(nodeData);
}
};
int main()
{
int testList[] = {1,2,3,4,5};
Node<int> rootNode(0);
for(int i = 0; i < 5; i++)
{
rootNode.append(testList[i]);
}
return 0;
}
You are creating newNode on the stack. After append has finished, it will no longer exist. By saying new Node(nodeData); you are essentially placing it on the heap, which gives you control over the objects life cycle.

Linked list and dynamic allocation frustration c++

Here's the reference code:
#include <iostream>
using namespace std;
class linkedList {
struct listNode{ //a node of a list
int value;
struct listNode *next;
};
listNode *head;
public:
linkedList(){
cout << "hello1\n";
head = NULL;
};
linkedList(listNode* a){
cout << "hello2\n";
head = a;
};
~linkedList();
listNode* getHead() {return head;}
void appendNode(int);
//inline Search function due to unable to function outside of class definition
listNode* rangeSearch(int a, int b){
//listNode to search
listNode *search = head;
//listNode* toReturn = new listNode;
//listNode to return list of values that are found within range
linkedList *found = new linkedList;
while(search){
//if the current value is within range, then add to list
if(search->value >= a && search->value <= b){
//append searched value onto found
found->appendNode(search->value);
//after appending, go to next value
}
search = search->next;
}
return found->getHead();
}
void display();
};
int main()
{
cout << "Programmer : n\n";
cout << "Description : \n";
linkedList* list = new linkedList;
int x = 12;
//values to search
int s1 = 10, s2 = 14;
// adds 2 to each number on list for 5 times
for(int i = 0; i < 5; i++){
list->appendNode(x);
x += 2;
}
//create something to hold pointer of found to be deleted when done using
//print list
cout << "Original set of numbers in linked list: ";
list->display();
cout << "\nThe following are the values withing ranges: " << s1 << " and " << s2 << ":\n";
//EDITED:
//list->rangeSearch(s1,s2);
linkedList foundList(list->rangeSearch(s1,s2));
foundList.display();
//End of edit 6:40PM 7/18/13
cout << "\nHere are the original set of numbers in linked list (again): ";
list->display();
delete list;
return 0;
}
void linkedList::appendNode(int newValue)
{
listNode *newNode = new listNode(); // To point to a new node
listNode *nodePtr; // To move through the list
// Allocate a new node and store newValue there.
newNode->value = newValue;
newNode->next = 0;
// If there are no nodes in the list
// make newNode the first node.
if (!head)
head = newNode;
else // Otherwise, insert newNode at end.
{
// Initialize nodePtr to head of list.
nodePtr = head;
// Find the last node in the list.
while (nodePtr->next)
nodePtr = nodePtr->next;
// Insert newNode as the last node.
nodePtr->next = newNode;
}
}
void linkedList::display() {
for(listNode* p = head; p != NULL; p = p->next)
cout << p->value << ' ';
}
linkedList::~linkedList()
{
cout << "\ndestructor called";
listNode *nodePtr; // To traverse the list
listNode *nextNode; // To point to the next node
// Position nodePtr at the head of the list.
nodePtr = head;
// While nodePtr is not at the end of the list...
while (nodePtr != NULL)
{
// Save a pointer to the next node.
nextNode = nodePtr->next;
// Delete the current node.
delete nodePtr;
// Position nodePtr at the next node.
nodePtr = nextNode;
}
}
So a couple of questions here. First, why is it when I try to put the rangeSearch member function outside of the class definition, the compiler gives an error saying listNode* type is not recognized?
Second, this has to do with destructors. In this program, 2 instances (list & found list) were created but only 1 destructor was called. Can someone explain why? My intuition tells me that the dynamically allocated pointer to linkedList object did not get destructed. However, I don't know why. The reason I had to use dynamically allocated memory is primarily because I want to pass the pointer back to the main function. If I don't, when rangeSearch exits, the pointer will be passed back to main but whatever list the pointer had would be deconstructed after
return ptr; (assume ptr is a pointer to a linkedList declared in rangeSearch)
which will cause my program to crash because, now the address has nothing in it and I'm trying to call... nothing.
Well, as usual I would appreciate whoever the great Samaritan out there who would be more than willing to educate me more about this.
First, you are having an issue with scoping. In C++, the curly braces define a new scope, so you are defining listNode inside the class linkedlist. If you want to access it, you'd have to use the scoping operator as such linkedlist::listNode
I don't entirely understand your second question. I only see one call to delete, so why do you think two destructors will be called? The destructors are only called when you call delete, so unless you specify that you want to destroy it, it's still going to be there.
Although I don't entirely understand your question, I see that you returned a pointer to the head in rangeSearch, but you don't assign it to anything. What this means is that you will have a memory leak; you allocated memory for the found, but then don't do anything with it. Actually since you only return the head, you still wouldn't be able to delete it if you did assign something to it, because you wouldn't have access to linked list itself.
linkNode is nested inside of linkedList. Move listNode outside of the linkedList class, and you won't get the first error. Or you can use it's full declaration, linkedList::listNode. Also, if you leave linkNode nested, you will have to make it public.
In main, you can just say
linkedList list;
instead of
linkedList* list = new linkedList;
rangeSearch() is returning a value, but that value is never being assigned to anything in main(). rangeSearch() is allocating a linkedList, but it never gets deleted.

C++ allocating node pointers

I'm making a C++ Maze program using disjoint sets and the Union/Find operations.
I have a MakeSet(int x) function which creates a new Node for every integer element x in the maze. (i.e 1 to 16 for a 4x4 maze). Thus initially every element is in its own set.
My MakeSet looks like this
void Maze::MakeSet(int x)
{
Node *root = new Node;
root->label = x;
root->parent = NULL;
}
But in the CreateMaze() function I have to call MakeSet many times to get all the elements into their own set initially. Thus, the root will keep being overwritten. How do I dynamically allocate many different nodes? Should I keep them separated in an array?
You already allocate a new Node, you are just not keeping it. You need to change the function to return Node*, and store the results in an array or a vector.
Node* Maze::MakeSet(int x) {
Node *root = new Node;
root->label = x;
root->parent = NULL;
return root;
}
Node *nodes[16];
for (int i = 0 ; i != 16 ; i++) {
nodes[i] = Maze::MakeSet(i);
}
An even better approach would be to do Node initialization in a constructor:
Node::Node(int x) : label(x), parent(NULL) {
}
You can now create all sets in a loop, and store pointers to them in an array:
Node *nodes[16];
for (int i = 0 ; i != 16 ; i++) {
nodes[i] = new Node(i);
}
You can have a std::vector of Node objects or pointers as a class member:
class Maze
{
std::vector<Node> nodes;
};
or, if you must
class Maze
{
std::vector<Node*> nodes;
}
and add the created nodes:
void Maze::MakeSet(int x)
{
Node *root = new Node;
root->label = x;
root->parent = NULL;
nodes.push_back(root);
}
Note that you'll have to implement a destructor to clean up the memory when you're done. This also means you should have a copy constructor and assignment operator for Maze.

Segmentation Fault C++

#include <iostream>
#include "Student.h"
#include "SortedList.h"
using namespace std;
#define BOUNDS 100
int main() {
SortedList *list = new SortedList(); // points to the sorted list object
Student *create[BOUNDS]; // array to hold 100 student objects
int num = 100000; // holds different ID numbers
// fills an array with 100 students of various ID numbers
for (int i = 0; i < BOUNDS; i++) {
create[i] = new Student(num);
num += 10;
}
// insert all students into the sorted list
for (int i = 0; i < BOUNDS; i++)
list->insert(create[i]);
// removes each student from the list
num = 100000;
for (int i = 0; i < BOUNDS; i++) {
list->remove(num);
num += 10;
}
delete list;
return 0;
}
I am getting a seg fault with the previous code. Any insight as to why this is or how to possibly fix it would be appreciated. The seg fault is definitely caused by the delete list; line
UPDATE 1: Here is my SortedList destructor
/*
* Destructs this sorted list object
*/
SortedList::~SortedList() {
freeList(head);
}
/*
* Traverses throught the linked list and deallocates each node
*/
void SortedList::freeList(Listnode *L) {
Listnode *tmp = L; //holds the node to be deleted
//traverses the list
while (tmp != NULL) {
Listnode *next = tmp->next; //holds the value of the next node
//delete previous node
delete tmp->student;
delete tmp->next;
delete tmp;
//sets the next node to the node to be deleted
tmp = next;
}
//delete header node
delete L;
}
Well, we can't see SortedList or Student, and I'd guess the problem is in one of those. I note that num never gets reset to its original value after the creation loop, which means that most of the remove calls are going to be passed an id that belongs to no Student; perhaps that case fails. Or perhaps there are simply bugs in the insert or remove methods -- or the constructor or destructor, for that matter. It's totally up in the air.
EDIT: As others have pointed out, that destructor uses a pointer after it's been deleted; that could be the only source of error, or there could easily be more in the code we haven't seen yet.
In freelist(), you delete tmp->next, then set tmp = tmp->next. Now tmp has an invalid pointer. You need to restructure your code so that you do not free a pointer before accessing its members.
Although I hate doing people's homework for them, here's my solution:
/*
* Traverses throught the linked list and deallocates each node
*/
void SortedList::freeList(Listnode *L) {
if(L == NULL) return;
freeList(L->next);
delete L->student;
delete L;
}
This use O(n) stack space for deletion, but I personally find it much clearer than a loop. Your solution can be tweaked to "just work" by removing the call to delete tmp->next.
// removes each student from the list
for (int i = 0; i < BOUNDS; i++) {
list->remove(num);
num += 10;
}
Looks interesting... how does this work exactly? If num is 100000 + BOUNDS*10 at this point in the code (since it is never changed after you add 10 to it for each student you create). Every remove call you make doesn't remove a student by their ID (since the id called is 100000 + BOUNDS*10 + i*10). Was the intent to remove them by ID, if so you should consider resetting num to 100000 before doing the remove loop.
To clarify how this could cause the seg-fault: if your remove function doesn't have proper bounds checking it could go out of the memory looking for the id to remove.
Updated with destructor question:
void SortedList::freeList(Listnode *L) {
Listnode *tmp = L; //holds the node to be deleted
//traverses the list
while (tmp != NULL) {
Listnode *next = tmp->next; //holds the value of the next node
//delete previous node
delete tmp->student;
delete tmp->next;
delete tmp;
//sets the next node to the node to be deleted
//**********
//Check here, you deleted next, but the assigned it to temp. Tmp isn't null, but
//it is however, no longer your memory (since you deleted it)
//**********
tmp = next;
}
//delete header node
delete L;
}