I am having a few simple troubles regarding my code. What I am trying to do is build my own "list" code for 2 reasons. First of all, I want to get a deeper understanding of how linked lists work in practice. And secondly, because I am working on a project that requires a list of references, not just copied objects like the standard "list" library offers. I am also attempting to keep my code similiar to how C# works, while keeping the simplicity of python's naming scheme.
The following code is my issue:
// & - Get the address of object
// * - Get the contents of pointer
// int x = 25;
// int* p = &x;
// "p" now contains the pointer address
// "*p" contains the same info as x
// The following are all equal
// x = x + 5;
// x = *p + 5;
// *p = *p + 5;
template <class T> class Link;
template <class T> class Link {
public:
T result;
bool containsData = false;
Link<T> *x;
};
template <class T> class list {
public:
Link<T> start;
int count = 0;
Link<T> *blank(T object) {
Link<T> temp;
return &temp;
}
void append(T object) {
print("Starting");
Link<T> *temp = &start;
while (true) {
print("1");
if (temp->containsData == false) {
break;
}
print("2");
Link<T> *next = temp->x;
temp = next;
}
print("Doing");
temp->containsData = true;
temp->x = blank(object);
print("Done");
count++;
}
void pop(int index);
void clear();
void reverse();
T get(int index);
};
What is happening is that the first iteration works well. However, the second appended item in the list seems to halt at "1", seeming to be completely incapable of moving past "temp->containsData". I've tried putting it in a try-catch block, but it would seem that not even try-catch picks it up for some reason (Which surprised me quite a bit!). It just exits in the middle of trying to find out if the next item in the list is just another link to search through, or whether it is the last one in the list (Hence, containsData set to false).
I have checked, and yes it is the "temp->containsData" part that just crashes out. It seems trying to access "containsData" randomly crashes it. I am aware the code isn't too pretty, that's mainly from my debugging.
If anyone could throw me some pointers (Pun not-intended) as to how to continue, I would be incredibly grateful. Again, I do not want to just use the inbuilt "list" library and call it a day. I want to legit work through this coding trouble.
Thanking you in advance
Andrey :)
Link<T> *blank(T object) {
Link<T> temp;
return &temp;
}
is bad because it is returning a pointer to non-static local object, which will vanish on returning from this function.
Try this instead:
Link<T> *blank(T object) {
return new Link<T>();
}
Just like magic, the moment I post this, THEN stackoverflow decides to point me to a useful post. I've been looking online for hours!
Thanks for the (Surprisingly) quick responses! I managed to find a better way of doing it. Turns out that I was goind about it a bit of an unnecessarily complicated way. The following code seems to work:
template <class T> class Link {
public:
Link<T> *next;
T *item;
};
template <class T> class list {
public:
Link<T> *start = new Link<T>();
int count = 0;
void append(T *object) {
Link<T> *currentValue = start;
while (currentValue->next != nullptr) {
currentValue = currentValue->next;
}
currentValue->next = new Link<T>();
currentValue->item = object;
print(object);
count++;
}
void pop(int index) {
if (index >= count) { throw "index larger than size!"; }
if (index == 0) {
//Deleting the first item in the list
if (count == 1) {
start = new Link<T>();
} else {
start = start->next;
}
} else if (index == count - 1){
//Deleting the last item in the list
Link<T> *currentValue = start;
while (currentValue->next->next != nullptr) {
currentValue = currentValue->next;
}
currentValue->next = new Link<T>();
} else if (index == count - 1) {
//Deleting somewhere in the middle of the list
}
count--;
}
void clear() { start = new Link<T>(); count = 0; }
T get(int index) {
if (index >= count) { throw "index larger than size!"; }
int looper = 0;
Link<T> *currentValue = start;
while (looper != index) {
currentValue = currentValue->next;
looper++;
}
T *temp = currentValue->item;
return *temp;
}
};
Much smaller, works fine, links everything properly.
HOWEVER, thankyou to all the people pointing out that a pointer will disappear once the reference to it disappears. I completely forgot about that.
Thankyou so much for all your help, and I shall (hopefully) be able to finish this code pretty quickly :)
Related
I am attempting to create a peek()/top() function for my Stack class using templates, and have this issue where I am unsure of how to return the topmost object of the stack. So far, I have been able to check if the top object is empty, if it's not then move a current pointer to point to the head of the Stack and return that pointer's data. However, this returns an int and I am wanting to return a reference to the object, not the data in the object. Any ideas of how to do this?
Stack.h
#include "LinkedList.h"
#include <cstdlib>
#include <iostream>
template <typename T>
class LStack
{
public:
//mutator member functions
LStack(){numofstacks = 0;}
~LStack(){}
void push(const T& obj) //insert obj at the top of the stack
{
data.add_to_head(obj);
numofstacks = numofstacks + 1;
}
T pop() //remove and return the top object from the stack, error if stack is empty
{
if (is_empty()){
std::cout << "Stack is empty" << std::endl;
exit(0);
}
else
{
data.remove_from_head();
numofstacks = numofstacks - 1;
if (is_empty()){
exit(0);
}
else{
data.move_to_head();
return data.get_current();
}
}
}
//return a reference to the ***OBJECT** at the top of the stack, NULL if the stack is empty, (also refered to as top())
T& peek()
{
if (is_empty()){
std::cout << "Stack is empty" << std::endl;
exit(0);
}
else{
data.move_to_head();
return data.get_current(); // Need to return reference to object here
}
}
bool is_empty() const //return a boolean indicating whether the stack is empty
{
return (size() == 0);
}
int size() const //return the number of objects in the stack
{
return numofstacks;
}
private:
LinkedList<T> data;
int used;
int numofstacks;
};
LinkedList
#include "Node.h"
#include <cstdlib>
#include <iostream>
template <typename T>
class LinkedList
{
public:
LinkedList() //constructor
{
head = NULL;
tail = NULL;
current = NULL;
list_length=0;
}
~LinkedList()//destructor since we're creating the linked list on the heap
{
while (head != NULL)
{
remove_from_head();
}
tail = NULL;//not sure if in or out of while loop
}
LinkedList(T& item)
{
head = new Node<T>(item);
tail = head;
current = tail;
list_length = 1;
}
void add_to_head(const T& item)
{
if (list_length == 0){ //if list is empty
head = new Node<T>(item);
tail = head;
current = tail;
list_length = 1;
}
else //Else if list is not empty.. Insert node at the head
{
Node<T>* head_insert = new Node<T>(item);
head->set_prev(head_insert);
head_insert->set_next(head);
head = head_insert;
list_length++;
head_insert = NULL;
}
}
void add_to_tail(const T& item)
{
if (list_length == 0){
head = new Node<T>(item);
tail = head;
current = tail;
list_length = 1;
}
else //insert node at tail
{
Node<T>* tail_insert = new Node<T>(item);
tail->set_next(tail_insert);
tail_insert->set_prev(tail);
tail = tail_insert;
list_length++;
tail_insert = NULL;
}
}
void remove_from_head()
{
if (list_length == 0){
return;
}
else if (list_length == 1){
delete head;
head = NULL;
tail = NULL;
current = NULL;
list_length--;
return;
}
else
{
Node<T>* temp_head = head;
head = temp_head->get_next();
delete temp_head;
list_length--;
temp_head = NULL;
}
}
void remove_from_tail()
{
if (list_length == 0){
return;
}
else if (list_length == 1)
{
delete head;
head = NULL;
tail = NULL;
current = NULL;
list_length--;
return;
}
else
{
Node<T>* temp_tail = tail;
tail = temp_tail->get_prev();
delete temp_tail;
list_length--;
temp_tail = NULL;
}
}
std::size_t size()
{
return list_length;
}
T get_current()
{
return current->get_data();
}
void forward()
{
current = current->get_next();
}
void back()
{
current = current->get_prev();
}
void move_to_head()
{
current = head;
}
void move_to_tail()
{
current = tail;
}
private: //private members
Node<T>* head; //point to start of list
Node<T>* tail; //point to end of list
Node<T>* current; //optional - used to refer to a node in our list
std::size_t list_length;
};
Error Message
In file included from main.cpp:1:0: LStack.h: In instantiation of ‘T&
LStack::peek() [with T = int]’: main.cpp:29:19: required from
here LStack.h:52:28: error: cannot bind non-const lvalue reference of
type ‘int&’ to an rvalue of type ‘int’
return data.get_current(); // Need to return reference to object here
This method in your linked list:
T get_current()
returns an rvalue of type T. rvalues are constant by definition. Change to prototype of get_current to:
T& get_current()
You may also need to change the return type for Node::get_data to return a reference as well.
The short answer is that as much as it may seem to you like you want to do this, you really don't, because there's no way to do it cleanly. Even having pop() return the top object by value is highly problematic. In particular, if the copy constructor for the stored type can throw, it's pretty much a disaster waiting to happen--if the copy constructor throws as its trying to copy the return value, the situation is basically unrecoverable. The value has already been removed from the stack, but it isn't being returned to the caller, so it's completely lost.
The standard library gets around this by not having pop return anything. So, you pop an item from the stack in two steps: first you use top() to get the top item on the stack, then you use pop() to just remove the item on top of the stack:
auto top_item = your_stack.top();
your_stack.pop();
If the copy constructor throws as you're trying to retrieve the top-of-stack item, then the your_stack.pop() won't execute, so the stack hasn't been affected--the top item remains there. The pop() only executes if you've already successfully retrieved the top item into top_item.
Unfortunately, this is a little clumsy to use.
Even more unfortunately, since we're using two steps to pop the top item, it's essentially impossible to make this thread-safe--for that, you just about have to ensure that retrieving the item and removing it from the stack are a single atomic operation--either both of them happen without anything else interfering, or else neither one happens at all.
There is a design that meets that requirement, and (at least arguably) isn't quite as clumsy to use:
void pop(T &t) {
t = storage.back();
storage.pop_back();
}
Much like the previous example using the standard stack, if the t=storage.back(); line throws an exception, then the storage.pop_back(); simply won't execute--you haven't retrieved an item, but the items on the stack remain undisturbed.
For a simple concurrent stack, you could add a mutex and change the return type to bool, indicating whether the operation succeeded or not (again, you can't follow the typical single-threaded pattern of "if (!stack.empty()) stack.pop()`, since you're back to using two separate operations, so even if you find a non-empty stack, by the time you try to pop it, some other thread may have already done so, and your attempt at popping will fail.
I am trying to dig into data structures in C++. Therefore I am learning how to write a list. Everything seemed to be working just fine until it came to overloading sum operator +. For two given lists it sums two of the highest values of the lists.
Here is .h file:
typedef struct rob{
int value;
struct rob* next;
}element;
class list{
public:
friend list& operator+(list&,list&);
friend void merge(list& x,list& y);
void show();
bool search(int x);
void append(int x);
bool sortAppend(int x);
list& operator--(int);
bool empty() { return (inf.head==nullptr);}
void clear() { inf.head = nullptr; }
list() { inf.head = inf.tail = nullptr; }
~list() { while(!empty()) { (*this)--;}}
private:
typedef struct{
element* head;
element* tail;
}info;
info inf;
};
I know that in an .h file the typedef may seem a bit C-like, but the header design is copied from the book that I am learning from. I am trying to crack the methods by my own though using authors ideas.
And relevant function definitions:
#include "list.h"
bool list::sortAppend(int x){
element* newElem = new element;
newElem->value = x;
if (empty()){
inf.head=inf.tail=newElem;
newElem->next=nullptr;
return true;
}
else if ( (newElem->value) < (inf.head->value) ){
newElem->next=inf.head;
inf.head=newElem;
return true;
}
else if ( (newElem->value) > (inf.tail->value) ) {
newElem->next=nullptr;
inf.tail->next=newElem;
return true;
}
element* tempHead = inf.head;
while(tempHead!=inf.tail){
if ( (newElem->value) < (tempHead->next)->value) {
newElem->next = (tempHead->next);
tempHead->next = newElem;
return true;
}
else{
tempHead = tempHead->next;
}
}
return false;
}
list& operator+(list& X, list& Y){
list* tempListArr[2] = {&X, &Y};
list* tempList = new list;
for(const list* i: tempListArr)
{
element* tempHead = (i->inf).head;
while(tempHead!= nullptr){
tempList->sortAppend(tempHead->value);
tempHead = tempHead->next;
}
tempList->show();
std::cout << "--\n";
}
return *tempList;
}
For given list containing values:
#include <iostream>
#include "list.cpp"
int main(){
list myList;
myList.sortAppend(5);
myList.sortAppend(2);
myList.sortAppend(4);
list myList2;
myList2.sortAppend(21);
list myList3;
myList3 = myList + myList2;
return 0;
}
Could anyone point me where I made a mistake? I am stuck for a few hours now and I don't know what goes wrong.
Many thanks in advance!
FOLLOW UP:
The sortAppend method surely works. It does create a sorted list as desired.
There must have been something wrong with the + operator definition itself though I have tried, instead of range loop, using for loop for one iteration and still I got a list of two values only.
You are simply not setting inf.tail to the new tail in
else if ((newElem->value) > (inf.tail->value)) {
newElem->next = nullptr;
inf.tail->next = newElem;
inf.tail = newElem; // <-- missing!
return true;
}
You should - at least - change the signature of operator+ to return a list instead of a list reference and return a local object instead of an unowned heap object (which is a memory leak). If you do so you will have to write a copy constructor and copy assignment operator too.
Given your code,
list myListWierd;
myListWierd.sortAppend(2);
myListWierd.sortAppend(4);
myListWierd.sortAppend(5);
myListWierd.show();
shows
2
5
so the sortAppend does not work.
The trouble is around updating either the tail, since operator + relies on using the tail.
I could sort the code out for you and make it work; indeed Andreas' answer does this. But for now, notice you have assumed a function works, but I found a case it doesn't work for by looking at the moving parts - a list we created, that we then try to re-create in a different order. As a general rule, try all the parts in a function that goes wrong, one at a time, maybe as a unit test.
Rather than fixing this, for now, let's make a couple of suggestions.
First, the destructor does nothing, other than walk pointers (using empty which uses head and not tail - so head needs setting as said before)
~list() { while (!empty()) { (*this)--; } }
If you don't want leaks you need to give this more thought.
Next,
list& operator+(list&, list&)
creates a pointer and returns its contents. This is a BAD IDEA. NEVER DO THIS.
For now, change the signature to
list operator+(list&, list&);
and just return a list:
list operator+(list& X, list& Y) {
list* tempListArr[2] = { &X, &Y };
list tempList;//copy it over to the calling vode otherwise DANGER
for (const list* i : tempListArr)
{
element* tempHead = (i->inf).head;
while (tempHead != nullptr) {
tempList.sortAppend(tempHead->value);
std::cout << "Adding " << tempHead->value << '\n';
tempHead = tempHead->next;
}
tempList.show();
std::cout << "--\n";
}
return tempList;
}
I'm trying to implement a basic hashtable. I'm using a linked list to resolve collisions.
My get and set methods are giving me a bunch of trouble, and I'm not really sure what the issue is. I believe I'm overloading the operators correctly. I think the problem happens when I append to my linked list.
class HashTable {
struct Node{
int key;
int value;
Node *next;
};
Node **table;
int hash_func(int key) const {
return key % TABLE_SIZE;
}
public:
HashTable() {
table = new Node*[TABLE_SIZE]();
for (int i = 0; i < TABLE_SIZE; ++i)
table[i] = nullptr;
}
int& operator[](int const key) {
int h_key = hash_func(key);
while(table[h_key]) {
table[h_key] = table[h_key]->next;
}
table[h_key] = new Node;
table[h_key]->key = key;
table[h_key]->next = table[h_key];
return table[h_key]->value;
}
int operator[](int const key) const {
int h_key = hash_func(key);
while (table[h_key]) {
if (table[h_key]->key == key) {
return table[h_key]->value;
}
table[h_key] = table[h_key]->next;
}
return 0;
}
};
Your problem is that you are overwriting data in your while loops in your get and set methods.
When you do table[h_key] = table[h_key]->next;, you permanently lose whatever was originally stored at location h_key. Instead use a placeholder, like this:
Node * curr = table[h_key];
while (curr->next)
{
curr = curr->next;
}
Node * new_node = new Node;
new_node->key = key;
curr->next = new_node;
You have a similar problem with your get method.
In your setter, you want to insert a new element at the end. So first you find the end like this:
while(table[h_key]) {
table[h_key] = table[h_key]->next;
}
But then, you set:
table[h_key]->next = table[h_key];
Instead, you would have to set:
table[h_key]->next = nullptr;
Otherwise, your condition in the loop does not work.
Consider this answer to an addendum to #bpachev's answer. My code only illustrates the problem using your wrong code, and is not a drop-in solution.
I agree with bpachev. Since this is C++, you should consider using templates for something like this (to removing clutter and reuse). Overhead for using template classes are minimal, and this process is done during compile time.
I wrote a simple console program for the use of testing some key classes in a library I'm building. Now, the code builds correctly with no errors. But, after executing the code, I found that the application stops working after calling the Index method at a certain point in the code. I tried debugging a few different ways to get more information about the problem, but the information I gathered didn't help me at all. Maybe it will help someone else who knows what I'm not doing(or doing incorrectly).
Here's the contents of the Util namespace;
template<typename var>
class VectorNode
{
public:
VectorNode(var value, VectorNode<var>* next = NULL, VectorNode<var>* prev = NULL)
{
data = value;
t_next = next;
t_prev = prev;
}
~VectorNode()
{
if (t_next != NULL)
delete t_next;
}
virtual VectorNode<var>* Next(){ return t_next; } // get the next node in line
virtual void Next(VectorNode<var>* newNode){ t_next = newNode; } // set the next node in line
virtual VectorNode<var>* Prev(){ return t_prev; }// get the previous node in line
virtual void Prev(VectorNode<var>* newNode){ t_prev = newNode; } // set the previous node in line
virtual var Value(){ return data; } // get the node's value
private:
var data;
VectorNode<var>* t_next;
VectorNode<var>* t_prev;
};
template<typename var>
class Vector
{
public:
Vector()
{
tailNode = new VectorNode<var>(*(new var));
headNode = new VectorNode<var>(*(new var), tailNode);
tailNode->Prev(headNode);
size = new int;
*size = 0;
}
~Vector()
{
delete headNode;
delete size;
}
int Size(){ return *size; } // get the size of a vector
void Add(var toAdd, int index = 0) //
{
VectorNode<var>* lastNode;
if (index > (*size))
index = *size;
if (index < 1) // add to the end of the vector
{
lastNode = tailNode;
}
else
{
int i;
if (index <= (*size / 2)) // if the index is less than half the size, iterate forwards
{
lastNode = headNode;
for (i = 1; i <= index; i++){ lastNode = lastNode->Next(); }
}
else // otherwise, iterate backwards
{
lastNode = tailNode;
for (i = *size; i >= index; i--){ lastNode = lastNode->Prev(); }
}
}
VectorNode<var>* temp = lastNode->Prev();
VectorNode<var>* newNode = new VectorNode<var>(toAdd, lastNode, temp);
lastNode->Prev(newNode);
temp->Next(newNode);
*size = *size + 1;
}
void Remove(int index) // remove an index
{
VectorNode<var>* toRemove;
VectorNode<var>* lastNode;
int i;
if ((index > *size) || (index < 1)) // if not in the domain...
index = *size;
if (index <= (*size / 2)) // iterate forwards
{
lastNode = headNode;
for (i = 1; i < index+2; i++){ lastNode = lastNode->Next(); }
}
else // iterate backwards
{
lastNode = tailNode;
for (i = *size; i > index; i--){ lastNode = lastNode->Prev(); }
}
toRemove = lastNode->Prev();
VectorNode<var>* temp = toRemove->Prev();
temp->Next(lastNode);
lastNode->Prev(temp);
delete toRemove;
*size = *size - 1;
}
var Index(int index) // get the value of a node
{
VectorNode<var>* lastNode;
int i;
if (index <= (*size / 2)) // iterate forwards
{
lastNode = headNode;
for (i = 1; i <= index; i++){ lastNode = lastNode->Next(); }
}
else // iterate backwards
{
lastNode = tailNode;
for (i = *size; i >= index; i--){ lastNode = lastNode->Prev();}
}
return lastNode->Value();
}
private:
int* size;
VectorNode<var>* tailNode; // the head and tail nodes are placeholders, to keep the list inside its boundaries
VectorNode<var>* headNode;
};
If you don't feel like reading that, I marked each method with a comment, explaining its overall purpose. Also, I tried adding a small explanation of some of my blocks of code.
And, here's the entry function, and inclusions;
#include "iostream"
#include "stdlib.h" // this has nothing in it that's being used
#include "testhead.h" // the location of the Util namespace
int main()
{
using namespace Util;
Vector<int>* x = new Vector<int>();
x->Add(42);
x->Add(24);
x->Add(12);
x->Add(21);
std::cout << "Listing Indices\n";
for (int i = 1; i <= x->Size(); i++)
{
std::cout << i << "\t" << x->Index(i) << "\n";
}
std::cout << "Size(pre-removal):\t" << x->Size() << "\n";
x->Remove(2);
std::cout << "Size(post-removal):\t" << x->Size() << "\n";
std::cout << "Listing Indices\n";
std::cout << 3 << "\t" << x->Index(3) << "\n";
for (int i = 1; i <= x->Size(); i++)
{
std::cout << i << "\t" << x->Index(i) << "\n";
}
system("Pause");
}
Okay, the results I got where this. Before using the Remove method, any index can be accessed from the Vector class freely. But, after using the remove method, no matter what index is removed, no index above one can be accessed. Except, in the case which we remove the first index, then no indices can be accessed. I tried stepping through the code, but it brought me up to this line of code in the index method;
else
{
lastNode = tailNode;
for (i = *size; i >= index; i--){ lastNode = lastNode->Prev();} // error occurs after running this line
}
Now, since I was able to figure out the Remove method was causing the problem, I went back and got some output about that. I had it run the following line prior to finishing its execution, twice. Once before toRemove is deleted, and once again after it is deleted.
std::cout << (lastNode->Prev() == temp) << "\t" << (temp->Next() == lastNode) << "\n";
Before it is removed, it prints 1 twice, indicating the comparison was true. But, the second time I call either the Prev and Next method, and the program freezes. I know this is because I freed the place in memory, but the comparison shows that any references from other nodes to the node I removed were gone. Now, my specific question is why exactly this is being caused, and how can I fix it? I know a little bit about managing memory on the heap, and this doesn't exactly appear as though it would cause any issues with the program. So, I could use a short explanation as to why this happens if anyone would be kind enough to provide it.
If it's any assistance, I'm using the Code::Blocks IDE and the GNU GCC compiler. Also, please tell me if I'm doing something wrong related to the way I asked my quetsion. I don't visit Stack Overflow often, and I don't ask questions here. This is just the best place to have your questions answered that I am aware of.
The destructor of the VectorNode class deletes the object pointer to by the t_next pointer. Calling delete on the toRemove pointer means that the destructor of that VectorNode object gets called, and then the next one, and then the next one etc. etc.
So basically, when you delete toRemove, you delete toRemove and all the objects that come after this. This causes the tailNode's t_prev to point to memory that you have already freed, and you then try dereferencing those pointers in your Index function, and that's not a good thing.
When you delete a VectorNode that has its t_next member pointing to some other node, the destructor of VectorNode will delete that other node (which in turn might go on to delete further nodes).
When you remove a node from the middle of a list with Remove(), the t_next of this node will point to the further nodes of the list. When this node is deleted, the destructor will also delete all the nodes following it in the list. Continuing to use this half-deleted list it will result in all kinds of problems.
Other random observations:
Why is size a int* instead of a normal int or size_t? I can't see any reason why this should be a pointer.
new VectorNode<var>(*(new var)) should really be new VectorNode<var>(var()) to not unnecessarily leak memory.
The t_next != NULL test before delete is unnecessary
Are you planning to create classes deriving from VectorNode<>? If not, than there is no reason why the methods would need to be virtual.
Using 1-based indexing in Add() is unusual, one would expect zero-based indexing
Also I feel obliged to tell you that there are standard library containers like std::list<> and std::vector<> which implement this kind of structures.
Calling remove deletes a node, but deleting a node deletes all the nodes->next
~VectorNode()
{
if (t_next != NULL)
delete t_next;
}
so deleting element 2 of your 1 based vector kills all the other elements as you have experienced, and there is no longer an element 3 to investigate
I'm a programming student in my first C++ class, and recently we covered linked lists, and we were given an assignment to implement a simple one. I have coded everything but my pop_back() function, which is supossed to return a pointer to the Node that needs to be deleted in Main(). No Node deletion is to be done in the actual function. So my question is:
Would you be willing to help point me in the right direction for my pop_back() function? Also, if you notice anything else that I'm doing wrong, let me know.
Also, this linked list is just to work with strings. In this case, a grocery list, so one string for the quantity of the item(1,2), and one string for the item type. (Milk, Eggs, etc.)
Below I've included my List & Node class implementations, so you can get an idea of what I've done so far.
Node.cpp
Node::Node(void)
{
descrip = " ";
quantity = " ";
previous = NULL;
next = NULL;
}
Node::Node(string q, string d)
{
descrip = d;
quantity = q;
previous = NULL;
next = NULL;
}
Node* Node::GetNext()
{
return next;
}
Node* Node::GetPrevious()
{
return previous;
}
void Node::SetNext(Node * setter)
{
next = setter;
}
void Node::SetPrevious(Node * setter)
{
previous = setter;
}
List.cpp
List::List(void)
{
first = NULL;
last = NULL;
numNodes = 0;
}
Node* List::GetFirst()
{
return first;
}
Node* List::GetLast()
{
return last;
}
void List::SetFirst(Node* setter)
{
first = setter;
}
void List::SetLast(Node* setter)
{
last = setter;
}
int List::GetNumNodes()
{
return numNodes;
}
void List::push_front(Node* item)
{
if (first == NULL)
{
first = item;
last = item;
}
else
{
Node* pFirst = first;
item->SetNext(pFirst);
first = item;
numNodes++;
}
}
void List::push_back(Node * item)
{
if (last == NULL)
{
first = item;
last = item;
}
else
{
last->SetNext(item);
last = item;
numNodes++;
}
}
Node* List::pop_front()
{
Node* temp = first;
first = first->GetNext();
if (first == NULL)
{
temp = first->GetNext();
first = p;
}
if (first == NULL)
{
last = NULL;
}
if (numNodes > 0)
{
numNodes--;
}
return temp;
}
Node* List::pop_back() // this whole function may be wrong, this is just my attempt at it
{
Node* temp;
temp = first;
while((temp->GetNext()) != NULL)
// im stuck here
}
Some pointers:
0x1243bfa3
0x45afc56e
0xdeadbeef
Some more pointers:
You should prefer to initialize your class members in the initialization list, not in the constructor's body.
In C++, unlike C89, we declare and define a function with no parameters as void f();, not void f(void);.
In C++ we commonly reset pointers with 0, not NULL.
See below for what I mean in code.
Good C++ code will try to take advantage of RAII. This implies avoiding primitive pointers for the most part. In this case plain old std::auto_ptr<> would make a perfectly sufficient substitute for the primitve Node* pointers. However, I do reckon part of the exercise here is pointer arithmetics, and so I just leave this as a side-note.
It would be useful for us if you'd attach the class declarations. I assumes all those accessors and mutators, GetFirst() and SetFirst() etc., are there because they are public. That's a bad idea. First, they expose the private pointers, which defeats the whole point of accessor. Second, they don't do anything special so they're just extra code -- which means extra room for bugs. This brings me to the next point.
Your mutators are incorrect. You blindly assign a new value to the private member pointer, without deleting what you had before. That's a memory leak.
Ever tried to pop_front() when the list is empty?
Finally, 8 being a round number it's time we get to the question at hand. pop_back(). My question to you is, why are you traversing the list all the way to the end if you so meticulously maintain a pointer to the last node of your list? Indeed, if you wouldn't bother with maintaining a pointer to the end of the list then you'd have to traverse all the way to the last node in order to pop it. And for that you were in the right direction. Except that ...
When you access members through pointers, as in first->GetNext(), always make sure first isn't a null pointer -- or else state in the function's documentation comment that you assume the pointer is not null.
These should get you started.
Points 1, 2 and 3 in code:
Node::Node()
: descrip(" "), quantity(" "), previous(0), next(0)
{
}
So if I understand this right you just want to run through your linked list until you get to the last node in the linked list and return the pointer to it?
I'm pretty sure what you have there will do it except
Node* List::pop_back() // this whole function may be wrong, this is just my attempt at it
{
Node* temp;
temp = first;
while(temp->GetNext() != NULL)
{
temp = temp->GetNext();
}
return temp;
}
So if I read it right, there it will continually loop around until it gets to the node with none in the line behind it, then return it.
I like the previous posters answer, but one thing you might want to keep in mind is if you have an empty list. Then your first pointer will equal NULL and you would be trying to call NULL->GetNext() basically and Seg Fault. I think you can edit the above code slightly and still get have it work like this:
Node* List::pop_back()
{
Node* temp;
temp = first;
while(temp != NULL && temp->GetNext() != NULL)
{
temp = temp->GetNext();
}
return temp;
}
This will have the function return NULL if there is nothing in the list and still work properly.
It would definitely have helped me if you also had posted your class declaration. I cannot guarantee that the below is correct but it makes sense to me
Node* List::pop_back()
{
Node *temp = NULL;
if(numNodes == 1)
{
temp = first;
// setting the list pointers to NULL
first = NULL;
// setting the list pointers to NULL
last = NULL;
//You should also probably remove the links from your node
//to the next and previous nodes but since you didn't specify
//this it is up to you
numNodes--;
}
else if(numNodes > 1) //more than one element
{
//the pointer you want to return
temp = last;
//For clarity I am creating another variable here
Node *newLast = temp->GetPrevious();
//Setting the new last node to point at nothing so now temp
//is "disconnected from the list"
newLast->next = NULL;
//the last pointer of the list is now pointing at the new last node
last = newLast;
//You should also probably remove the links from your node
//to the next and previous nodes but since you didn't specify this it is up to you
numNodes--; //decrement the counter
}
return temp;
}