#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;
}
Related
I am trying to learn constructors in c++. I am working on a list that I defined. I managed to get the copy constructor working, but I have problems with the array transfer constructor. Any help will be appreciated. Thanks!
The array transfer constructor supposedly should take in an array and a size(int) and output a list with that size.
ex: input: data = {1,3,5,6};int = 5;output = {1,3,5,6,0}
edit: change n to i
#include <iostream>
using namespace std;
class list_element{
public:
list_element(int n = 0,list_element* ptr = nullptr):
d(n),next(ptr){}
int d;
list_element* next;
};
class List{
public :
List():head(nullptr),cursor(nullptr){}
List(const int* arr, int n); // copy array transfer data
List(const List& lst); //copy constructor
void prepend(int n);
int get_element()
{
return cursor->d;
}
void advance() { cursor = cursor->next; }
void print();
~List(); //delete
private:
list_element* head;
list_element* cursor;
};
//transfer array
List::List(const int* arr, int n) {
List temp;
int i = 0;
while (i < n)
{
head = new list_element(arr[i], head);
++i;
}
}
//delete
List::~List(){
for (cursor = head; cursor != 0;)
{
cursor = head->next;
delete head;
head = cursor;
}
}
//deep copy constructor
List::List(const List& lst) {
if (lst.head == nullptr)
{
head = nullptr; cursor = nullptr;
}
else
{
cursor = lst.head;
list_element* h = new list_element();
list_element* previous;
head = h;
h->d = lst.head->d;
previous = h;
for (cursor = lst.head; cursor != 0;)
{
h = new list_element();
h->d = cursor->d;
previous->next = h;
cursor = cursor->next;
previous = h;
}
cursor = head;
}
}
void List::prepend(int n)
{
if (head == nullptr)
cursor = head = new list_element(n, head);
else
head = new list_element(n, head);
}
void List::print()
{
list_element* h = head;
while (h != 0)
{
cout << h->d << ',';
h = h->next;
}
cout << "###" << endl;
}
int main()
{
List a, b;
//change size
int data[10] = { 1,3,5,7};
List d(data, 10);
d.print();
return 0;
}
Main Question
With regards to your 'from_array' constructor, you have a temporary List variable that you are not using and is also unnecessary.
Second you are assigning the head pointer each time meaning that by the end of the constructor call, head now points to the last element you constructed.
Third your list_element constructor is taking the old head pointer which points to the previous element meaning the list is tries to advance from the bottom element upwards through the list, causing the reversed read you mentioned in a comment.
You can fix this two ways. First, you could reverse the order you read the input array so it constructs your linked-list back to front.
List::List(const int* arr, int n)
{
int i = n - 1;
list_element* it = new list_element(arr[i], nullptr); ///< Last element
--i;
while (i > -1)
{
it = new list_element(arr[i], it);
--i;
}
head = it; ///< assign head to the last instance of it.
it = nullptr;
}
However, there is a better why that expresses the linking of the elements more intuitively. First you need to pre-construct the next element and give it some default values (I didn't bother implementing a default constructor for list_element but you might want to.) and assign it to next, then assign head to a new list_element passing in the pointer to next. increment i so that you can assign next's value to the second value in the array. Finally incremenet i again so we can loop through the rest of the array. Finally, in the while loop, copy next into a variable called prev. Assign next to a new list_element with the value from the array and a nullptr. Finally assign prev->next to the new next pointer and increment i.
list_element::list_element()
List::List(const int* arr, int n)
{
int i = 0;
list_element* next = new list_element(0, nullptr);
head = new list_element(arr[i], next);
++i;
next->d = arr[i];
++i;
while (i < n)
{
list_element* prev = next;
next = new list_element(arr[i], nullptr);
prev->next = next;
++i;
}
}
Side Notes
Because you stated you are tying to learn about C++ constructors (and I'm assuming data structures) I would suggest starting with a static array type similar to std::array as it's constructors a bit more trivial to implement or even just start with simple classes/struct that just hold simple data like a few ints or whatnot as you can get an idea for the semantics around the various constructors in C++.
Also, the C++ standard library has two linked list types (std::list and std::foward_list)
Finally, you might be better off using a std::initializer_list instead of a raw array as this give you iterators to the data you want to copy which is a bit nicer to use.
Best of luck in your learning journey.
Try to avoid using variable names like 'n', which could be very confusing.
In your copy constructor for transferring the array, you should not access the array using 'n', which is the desired size of the transferred array, nor increment it.
Additionally, sizeof(arr) doesn't work as you would expect. You are querying the size of the pointer.
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 :)
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;
}
Simply, this program is to create a linked list of chars from a string. (say from "HELLO" to head->h->e->l->l->o->NULL)
Whenever I try to delete the head using my erase function, the program would stop working giving the "application.exe has stopped working... Windows is checking for a solution..". I think I might have problem with my memory allocation but I can't really tell. Suggestions greatly appreciated.
THIS WORKS
void StringADT::append(string s)
{
for (int i = 0; i < s.length(); i++)
{
Node* NodePtr;
Node* newNode;
newNode = new Node;
newNode->data = s.at(i);
newNode->next = NULL;
if (!head)
{
head = newNode;
} else
{
NodePtr = head;
while (NodePtr->next)
{
NodePtr = NodePtr->next;
}
NodePtr->next = newNode;
}
}
}
void StringADT::erase(int pos) //pos = position to erase
{
if (!head || pos < 0 || pos > length() - 1)
return;
else {
Node* NodePtr;
NodePtr = head;
if (pos == 0)
{
NodePtr = head->next;
delete head; //PROBLEM COMES AFTER EXECUTION OF THIS LINE!!
}
}
}
here is my class
class StringADT{
private:
struct Node {
char data;
Node* next;
};
Node* head;
here is my append function which may be the root of the problem because of memory allocation.
void StringADT::append(string s) (appending string s to the linked list)
{
Node* NodePtr;
int slength = s.length();
Node *NodeArray;
NodeArray = new Node[slength];
if(!NodeArray)
return;
for (unsigned i = 0; i < s.length(); i++)
{
NodeArray[i].data = s.at(i);
NodeArray[i].next = NULL;
}
if (!head)
{
head = NodeArray;
NodePtr = head;
for(unsigned count = 1; count < slength; count ++)
{
NodePtr->next = (NodeArray + count);
NodePtr = NodePtr->next;
//cout << "number of count " << count << endl;
}
} else {
NodePtr = head;
while (NodePtr->next)
{
NodePtr = NodePtr->next;
}
for(unsigned count = 0; count < slength; count ++)
{
NodePtr->next = (NodeArray + count);
NodePtr = NodePtr->next;
//cout << "number of count " << count << endl;
}
}
}
The erase function should be like this:
if (pos == 0)
{
NodePtr = head;
head = head->next;
delete NodePtr;
}
You allocated your Node objects as follows:
NodeArray = new Node[slength];
What you have there is an array of Node objects in one allocation. Later, when you're done with a particular element in that array, you're calling
delete head; //PROBLEM COMES AFTER EXECUTION OF THIS LINE!!
to delete a particular element of the array.
This is not allowed in C++ memory allocation.
If you've allocated an array, you can only delete the entire array, not specific elements. Moreover, you'll need to use the array delete[] operator, like this:
delete[] someArray;
But this gets to a fundamental problem with your design. You're implementing a linked list, but you're allocating an array. Now, in theory you can go ahead and allocate an array of linked list elements as you have done, but it doesn't make sense to do. An array is allocated for a specific number of elements; a linked list is often used when you need an arbitrary number of elements and have no idea what that number is.
Linked lists usually have their elements allocated and deleted one at a time, rather than sitting in a pre-defined array. When you get new data, you allocate a new node, then add it to the list.
(And the code smell above is, if you have an array, why do you need to link the elements? You could just iterate to the next element instead of following pointers. Arrays and linked lists just aren't used in combination; they have different purposes. For your linked list you should get rid of the array and allocate individual nodes, then you can delete them individually as well. You should not have that array at all.)
The problem is obviously that you allocate array of objects and than want to delete one of them. You have to allocate the Nodes independently. I.e. it's the append function you need to fix.
The append should be written as append(char), which just appends one char and than append(string const &) (strings may be expensive to copy, so always by reference, usually constant!) should just call that in a loop. And append(char const *), so you can append string literals too!
Don't use indices for things that don't have random access. And so that you don't have to think what has direct access, simply learn to prefer iterating by iterators/pointers over iterating with index!
I won't give examples, because for assignment it's better if you come up with the actual code yourself, even if you have to repeatedly ask for clarification.
I have a template class OList that is an ordered linked list (elements are ordered in ascending order). It has a function called void insert(const T & val) that inserts an element into the correct place in the list. For example, If I had an OList of ints with the values { 1,3,5 } and called insert(4), the 4 would be inserted between the 3 and the 5, making OList { 1,3,4,5 }.
Now, what I have works fine when inserting elements into EMPTY OLists. However, when I use the following code:
OList<char> list;
for (int i = 0; i < 3; i++) {
list.insert('C');
list.insert('A');
}
printInfo(list);
printList(list) should output:
List = { A,A,A,C,C,C } Size = 6 Range = A...C
Instead, it outputs:
List = { A,C,C,C,
followed by a runtime error.
I have been messing with this for about 5 hours now, but I don't seem to be making any progress (aside from getting DIFFERENT wrong outputs and errors).
There are three relevant pieces of code: OList's default constructor, operator<<, printInfo(), insert(), and a helper function for insert that finds the node to insert the element. I don't see any reason to provide operator<< nor printInfo() since these seem to work fine elsewhere.
// default constructor
OList() {
size = 0;
headNode = new Node<T>;
lastNode = new Node<T>;
headNode->next = lastNode;
lastNode->next = NULL;
}
void insert(const T & val) {
if ( isEmpty() ) {
lastNode->data = val;
}
else {
Node<T> * pre = headNode;
Node<T> * insertPoint = findInsertPoint(pre, val);
Node<T> * insertNode = new Node<T>;
insertNode->data = val;
insertNode->next = insertPoint;
pre->next = insertNode;
// why is pre equal to headNode?
// I thought I changed that when using it
// with findInsertPoint()
cout << (pre == headNode) << endl;
}
size++;
}
// returns the node AFTER the insertion point
// pre is the node BEFORE the insertion point
Node<T> * findInsertPoint(Node<T> * pre, const T & val) {
Node<T> * current = pre->next;
for (int i = 0; (i < getSize()) && (val > current->data); i++) {
pre = current;
current = current->next;
}
return current;
}
lastNode is simply the last node in the list.
headNode is a "dummy node" that contains no data and is only used as a starting place for the list.
Thanks in advanced. I'm really embarrassed to be asking for homework help on the internet, especially since I'm sure the main problem is my lack of a thorough understanding of pointers.
You are passing the pointer to pre by value into findInsertPoint, so it is copied, and the function changes the copy of pointer, and when the function returns, it is still the old pre, not the pre from inside the function.
If you want to change the pointer, you must pass pointer to the pointer to the function (or reference to pointer).