Read Access Violation when looping through LinkedList - c++

I am creating a planet simulation which makes use of a doubly linked list and several loops to calculate forces, collisions and so on. The issue I am having is a read access violation error when trying to delete a planet due to a collision.
When checking for a collision the smaller of the two planets is deleted, and the way I wrote it is that the smaller planet in the equation can be from the encompassing loop, which if deleted breaks the loop.
A combination of being new to C; staring at the same issue for days now; and that my lecturer for the class is making us use a C/C++ hybrid, is resulting in me struggling to think of an efficient way to fix the issue. Moving the loops out can and has solved the issue, but has a drastic effect on performance of the simulation.
The code can be seen below:
struct planet *head; //Head of list
struct planet *tail; //Tail of list
struct planet {
//Data
float mass;
struct planet *next;
struct planet *prev;
};
planet *remove(struct planet* p) {//Breaking the tree
if (p == head) {
removeHead(); //Method not included in sample due to size and it is sound.
}
else if (p == tail) {
removeTail();//Method not included in sample due to size and it is sound.
}
else {
p->prev->next = p->next;
p->next->prev = p->prev;
}
return p;
}
planet *destroy(struct planet* p) {
if (p) {
if (p != head || p != tail || (!p->next && p->prev)) {
delete p;
printf("Deleted\n");
return 0;
}
else {
printf("Not deleted\n");
return 0;
}
}
}
for (struct planet *p1 = head; p1 != 0; p1 = p1->next)
{
for (struct planet *p3 = head; p3 != 0; p3 = p3->next)
{
//Collision logic
if(p1 != p3){
if(p1->mass >= p3->mass){
destroy(remove(p3)); //Does not cause an error
break;
}else{
destroy(remove(p1)); //Causes the error.
break;
//Deleting p1 here means the for loop can't move on
}
}
}
}
What I am looking for is some advice on how to delete p1 efficiently and without breaking the loop.
Any help is greatly appreciated, and please forgive my less than clean code.

Breaking out of the inner loop when p1 is destroyed, will not break the outer loop, where p1 is dereferenced by the loop control, after it was deleted.
You can avoid it with code perhaps like this. I don't like using for loops with a linked list, and the while makes it easy to set up the next link.
struct planet *p1link, *p3link;
p1 = head;
while(p1 != NULL) {
p1link = p1->next; // collect next link now
p3 = p1->next; // avoid detecting B-A as well as A-B
while(p3 != NULL) {
p3link = p3->next; // collect next link now
//Collision logic
if(p1->mass >= p3->mass){
destroy(remove(p3));
} else {
destroy(remove(p1));
}
p3 = p3link; // next
}
p1 = p1link; // next
}
However the whole concept is flawed, because the p3 you delete might be the next p1 planet. So I suggest including a struct member pending and you make another parse of the list afterwards, to remove dead planets.

Related

Doing a exercise about bunny colony, hit a wall

I was doing last exercise from this list (its called graduation): http://www.cplusplus.com/forum/articles/12974/ but had one major problem. The code I wrote runs, but it will crash at so time (after deleting half bunnies), sometimes after program deletes half bunnies first time, sometimes after 10 such cycles, note that i havent implemented alot yet because i want to fix this bug, with your help of course. Also I know this is not code review, but some small tips about style and improving would be good too. So this is code i wrote so far:
Main.cpp:
include bunnyList.h
include windows.h
using namespace std;
int main(){
srand(time(NULL));
bunnyList Colony;
int turns = 0;
Colony.setUp();
while(Colony.getColonySize() > 0){
//New turn
Colony.increaseAgeAndKill();
Colony.breedBunnies();
std::cout << "Turn: "<< turns << ". Colony size: " << Colony.getColonySize() << std::endl;
//Get rid of these food eaters
if(Colony.getColonySize() > 1000){
std::cout << "500 bunnies died!" << std::endl;
Colony.killHalfBunnies();
}
Sleep(100);
turns++;;
}
}
bunnyList.h:
#ifndef BUNNYLIST_H
#define BUNNYLIST_H
#include <stdlib.h>
#include "node.h"
#include <time.h>
#include <iostream>
#include <string>
const int numOfNames = 4;
const int numOfColors = 4;
const int bunniesIni = 5;
const std::string colors[numOfColors] = {"Black", "White", "Brown", "Spotted"};
const std::string maleNames[numOfNames] = {"Joe", "Rafael", "Buby", "Messi"};
const std::string femaleNames[numOfNames] = {"Reichel", "Agnesa", "Mr Flufy", "Flower"};
class bunnyList{
private:
node *head;
int noOfBunnies;
node *current, *prev;
public:
bunnyList();
void newBunny(std::string);
void killHalfBunnies();
void increaseAgeAndKill();
void deleteNode();
void breedBunnies();
void setUp();
int getRandomNumber(int) const;
std::string getRandomColor();
std::string getRandomName(bool);
bool isMaleRandom();
int getColonySize() const;
};
#endif
bunnyList.cpp:
#include "bunnyList.h"
bunnyList::bunnyList(){
noOfBunnies = 0;
}
void bunnyList::setUp(){
std::string temp = "";
head = NULL;
for(int i = 0; i <= bunniesIni; i++){
newBunny(temp);
}
}
void bunnyList::killHalfBunnies(){
prev = head;
current = head;
while(noOfBunnies > 500){
if(getRandomNumber(2) == 1){
deleteNode();
continue;
} else if(current == NULL){
current = head;
prev = head;
} else {
prev = current;
current = current->next;
continue;
}
}
}
void bunnyList::newBunny(std::string color){
node *bunny = new node();
node *temp = head;
if(color == ""){
bunny->color = getRandomColor();
} else {
bunny->color = color;
}
bunny->isMale = isMaleRandom();
bunny->name = getRandomName(bunny->isMale);
bunny->age = 0;
bunny->next = NULL;
bunny->isBreedable = 0;
if(head == NULL){
head = bunny;
return;
}
while(temp->next != NULL){
temp = temp->next;
}
temp->next = bunny;
noOfBunnies++;
}
void bunnyList::increaseAgeAndKill(){
current = head;
prev = head;
while(current != NULL){
current->age++;
//Check if bunny can breed
if(current->age > 2){
current->isBreedable = 1;
}
//Check if its time to die :/
if(current->age > 10){
deleteNode();
}
prev = current;
current = current->next;
}
current = head;
prev = head;
}
void bunnyList::breedBunnies(){
node *temp = head;
bool oneMale = 0;
int femaleCount = 0;
//Check if there is at least one breedable male
while(temp!=NULL){
if(temp->isMale && temp->isBreedable){
oneMale = 1;
break;
}
temp = temp->next;
}
//For every female bunny over 2 years old a new bunny is born
temp = head;
if(oneMale){
while(temp != NULL){
if(temp->isMale == 0 && temp->isBreedable){
newBunny(temp->color);
}
temp = temp->next;
}
}
}
void bunnyList::deleteNode(){
if(current==head){
head = current->next;
prev = head;
delete current;
current = head;
noOfBunnies--;
} else if(current->next==NULL){
delete current;
prev->next = NULL;
prev = head;
current = head;
noOfBunnies--;
} else {
prev->next = current->next;
current->next = NULL;
delete current;
current = prev->next;
noOfBunnies--;
}
}
std::string bunnyList::getRandomName(bool isMale){
int r = getRandomNumber(numOfNames - 1);
if(isMale)
return maleNames[r];
return femaleNames[r];
}
std::string bunnyList::getRandomColor(){
int r = getRandomNumber(numOfColors - 1);
return colors[r];
}
bool bunnyList::isMaleRandom(){
if(getRandomNumber(2) == 1) {return true;}
return false;
}
int bunnyList::getRandomNumber(int limit) const{
return rand() % limit + 1;
}
int bunnyList::getColonySize() const{
return noOfBunnies;
}
node.h:
#ifndef NODE_H_INCLUDED
#define NODE_H_INCLUDED
#include <string>
class node {
friend class bunnyList;
private:
std::string name;
int age;
std::string color;
bool isMale;
node *next;
bool isBreedable;
public:
};
#endif // NODE_H_INCLUDED
Thank you for your help.
Since you asked for the review...
NEVER write using namespace std. Never. Just this morning there was a problem asked on SO where the reason for the issue at hand was that notorious line. I wonder who and why suggested that this is a good approach - there should be a book somewhere with this. If I had my way, it's author would be condemned to eternal manual removal of this line from every file.
Even without reading a line from the code, just by explanations alone, I know that the problem is most likely (100% likely, as in) to be related to memory management. You are freeing the memory which was not allocated properly, you are freeing the same memory twice or you are freeing something which was not allocated at all or you are accessing the memory after it was freed. Look at your deletes and check them.
On the style. Your code basically is an implementation of the business logic-aware list. Generally, this is not a good practice. It is much better to implement a generic list, supporting addition, deletion and other generic list operations, and than implement your business logic on top of this generic list.
Do not use current in your list. Instead, pass a node to be deleted in your delete function.
Lastly, run your program in the debugger and look into the variables you are deleting.
EDIT
Answering questions in commments.
Here is what I meant by business logic separation. There is a generic data structure, called list. It can be a list of anything, bunnies or space rockets, doesn't matter - but it still supports the basic list operations. Obviously, the two most important are insert and delete, but it is not the only operations for generic list. You can read wikipedia on list (data structure) for general ideas and look into std::list as in implementation. Now, you have your specific use case for list, a list of bunnies. For that specific use case you will add functionality on top of generic list. To clarify further, deleting an item from the list is something generic list supports. But 'killing a rabit' when the poor creature ages 10 years is something of the business logic. It contains iterating over list of rabbits (provided by generic list), checking age and making a decision to eliminate the creature (business-logic level) and than deleting the element (generic list). If this code were to be written using std::list, it would like approximately following:
std::list<Bunny> bunnies;
for (auto bunny = bunnies.cbegin(), end = bunnies.cend(); bunny != end; ++bunny) {
if (bunny->age() > 10)
bunny = bunnies.erase(bunny);
}
I got a crash too. It crashed in deleteNode, trying to reference current->next when current was NULL.
This was called from bunnyList::killHalfBunnies, in this code, which is the problem I find:
if (getRandomNumber(2) == 1){
deleteNode();
continue;
}
else if (current == NULL){
current = head;
prev = head;
}
The problem is you call deleteNode, which assumes that current is not NULL, before checking that current is in fact not NULL. I rearranged the if's as shown here, and I'm no longer getting a crash:
if (current == NULL)
{
current = head;
prev = head;
}
else if (getRandomNumber(2) == 1)
{
deleteNode();
continue;
}
It would also be wise to put a check inside deleteNode, so that if it is called when current is NULL, it can handle it. Possibly by throwing an exception or otherwise warning you.
Since you also asked about style: comments in deleteNode (and elsewhere) would make it all clearer!
I assume this is a class assignment, and that's why you're not using std::list.

Traversing linked list and modifying or inserting node C++

I am attempting to write a function that will traverse a linked list, wherein the nodes represent terms of a polynomial. Each node includes fields for coefficient (a double named coeff), power (a size_t named power), and link (a NodePtr *next). The function is called with a double variable value, which represents the coefficient the node should have, and a size_t variable i, which represents its power. The function should traverse the linked list looking for the node with power i. If the list already contains a node with power i, its coefficient should be changed to hold the new value. If it did not previously have a node with power i, such a term should be added with the coefficient value. The list should be ordered by power (i.e. the node with power 3 should be the node 3 in the list).
Below is the code I have written thus far, though it currently generates the following error:
Unhandled exception at 0x0130D2FA in Project 3.exe: 0xC0000005: Access violation writing location 0x0000000C.
I cannot figure out why the error is generated, so that is my first issue. The second is that I believe my function may have some logical errors and does not correctly modify and create new nodes.
I have been stumped on this for days and cannot test my other functions without this having this one in working order, so any help would be greatly appreciated! Thank you!
void Poly::setCoeff(double value, size_t i)
{
if (0 <= i){
Node* prev = new Node();
Node* curr = new Node();
Node* newNode = new Node();
newNode->coeff = value;
newNode->power = i;
curr = myHead; // Initialize curr to myHead;
if (curr != nullptr)
{
while (curr->next != nullptr && curr->power != i)
{
prev = curr;
curr = curr->next;
}
if (curr->power == i)
{
curr->coeff = value;
}
else if (curr->next == nullptr && i == curr->power + 1)
{
curr->next = new Node; // Creates a node at the end of the list
curr = curr->next; // Points to that node
curr->next = nullptr; // Prevents it from going any further
curr->power = i;
curr->coeff = value;
}
else
{
prev->next = newNode;
newNode->next = curr;
}
}
else
{
curr->next = newNode;
curr = curr->next;
}
}
else
{
throw std::out_of_range("Index out of range");
}
}
It is a series of clear incorrect assumptions of how dynamic memory is managed in C++ that is getting you into heap-loads of trouble in this code. Were this not an academic exercise I would tell you simply to throw it all away and use:
std::map<size_t, double>
also known as: The Good Stuff. It would do literally everything you need this code to accomplish.
But this is academia. Like most things in academia they make you crawl through trenches before you learn how it should be. So, I will expose what is deficient in your code, but suffice it to say, once you learn all this you will strive not to have to do it in the first place by using tools already available to you.
In other words, unless someone said I had to do this with a hand-coded linked list implementation, I would use the above map instead. You can't (yet), but know it is there.
Your Code
You didn't include the definition of Node, but I can only assume it looks something like this:
struct Node
{
double coeff;
size_t power;
Node *next;
};
Whether this is nested within class Poly or not (and it likely should be if the latter) is likewise unclear. It isn't entirely relevant to the question, but mentioned here to attempt to drive home that, when asking a question on SO, provide enough info to minimize assumptions that may affect the answers your getting.
With that your code:
void Poly::setCoeff(double value, size_t i)
{
if (0 <= i) // NOTE: not needed, unsigned, will always be i >= 0
{
Node* prev = new Node(); // NOTE: wrong. leaks memory.
Node* curr = new Node(); // NOTE: same as above
Node* newNode = new Node(); // NOTE: **may** leak (see below)
newNode->coeff = value;
newNode->power = i;
curr = myHead;
if (curr != nullptr) // OK: check for null good
{
// NOTE: should be checking `curr`, not curr->next
while (curr->next != nullptr && curr->power != i)
{
prev = curr;
curr = curr->next;
}
// NOTE: should check curr for NULL first.
if (curr->power == i)
{
curr->coeff = value;
}
// NOTE: same here. also,
else if (curr->next == nullptr && i == curr->power + 1)
{
// NOTE: this code path will leak newNode allocated at the
// top of the function.
curr->next = new Node;
curr = curr->next;
curr->next = nullptr;
curr->power = i;
curr->coeff = value;
}
else
{
prev->next = newNode;
newNode->next = curr;
}
}
else
{ // NOTE: this is where your mainline fault is coming from. you
// just validated curr can be NULL here (and will be on initial)
curr->next = newNode;
curr = curr->next;
}
}
// NOTE: this can't happen, as i can never be less than zero
else
{
throw std::out_of_range("Index out of range");
}
}
The following are somewhat obvious.
Your memory management is not correct, and includes introducing memory leaks.
Your pointer management is likewise poor. Pointers are not like Java references, and nothing will get you in trouble faster in a C/C++ program than improper pointer management.
The algorithm doesn't maintain the mandate the list be ordered.
Changes to Your Code
The requirements for your code mandate an ordered list is maintained, yet your coefficient insertion algorithm makes no attempts at fulfilling that requirement. The setCoeff member is required to insert a new term if the matching exponent cannot be found, and if kept sorted, you'll know by proper enumeration whether that is the case by discovering (a) an exponent beyond yours, or (b) the end of the list, whichever happens first.
i is a size_t value, which means it is a magnitude for object counting. The standard mandates size_t is unsigned, which means it cannot be negative. This means checking for i >= 0 is useless. It will always be so.
A new node is allocated before know you need one. Remember, this is supposed to update an existing node if you find a matching exponent entry. Only if there were no match should a new node be required.
Your first-insert detection needs a complete redeux. It is guaranteed to invoke undefined behavior.
First make it easier on yourself. Provide a Node constructor that sets up a node via parameters so you can stop littering your code with that setup. In doing so, it becomes both easier to read and safer, since you initialize all member variables at construction.
struct Node
{
Node *next;
double coeff;
size_t power;
Node(double coeff_, size_t power_, Node *next_=nullptr)
: coeff(coeff_), power(power_), next(next_)
{}
};
With that, things will get considerably easier. The punch list above can be fulfilled with the following changes:
void Poly::setCoeff(double value, size_t i)
{
Node *prev = nullptr; // points to prior node
Node *curr = myHead; // points to current node
while (curr && curr->power < i)
{
prev = curr; // remember current node...
curr = curr->next; // ... then move to next node
}
// only allocate a new node if
// (a) we reached the end of the list (curr == NULL)
// (b) we reached a node with non match (will be larger exponent)
if (!curr || curr->power != i)
{
// **NOW** allocate the new node. we know we need one and we
// have a pretty solid idea where it goes.
Node *newNode = new Node(value, i, curr);
// if prev is set, then it means the new node goes somewhere
// *past* the head pointer otherwise it will become the new head.
if (prev)
prev->next = newNode;
else
myHead = newNode;
}
else
{ // found matching node
curr->coeff = value;
}
}
I sincerely hope it helps, and wish you the best of luck in trenching through the cruft before you get to The Good Stuff. Its worth it in the end.
I will follow up with the answer using a std::map (as WhozCraig's excellent answer mentions):
#include <map>
#include <iostream>
typedef std::map<size_t, double> Polynomial;
void AddCoefficientAndPower(Polynomial& poly, double coeff, size_t power)
{
// This does everything your assignment asked for, except for implementing
// all of that linked list stuff
poly[power] = coeff;
}
using namespace std;
int main()
{
Polynomial myPoly;
// add the coefficient and power
AddCoefficientAndPower(myPoly, 3, 1);
AddCoefficientAndPower(myPoly, 4, 2);
AddCoefficientAndPower(myPoly, 9, 0);
AddCoefficientAndPower(myPoly, 6, 3);
// This one will replace the previous (4,2)
AddCoefficientAndPower(myPoly, 3, 2);
// write out the coefficients followed by the power
Polynomial::iterator it = myPoly.begin();
while (it != myPoly.end())
{
cout << it->second << "^" << it->first << "\n";
++it;
}
}
Output:
9^0
3^1
3^2
6^3
Basically your entire assignment is a one line C++ statement in AddCoefficent that inserts an item in the map, and replaces an existing entry if one did exist.
Note -- no memory leaks, no calls to new, no crashes, etc.
Also, if your requirements were to also include any integral power value, then the above method works for negative, 0, and positive power values.

Segfault when accessing non-null pointer?

The code works fine in a Linux environment, but in Windows it crashes 5-10 seconds after the program starts. The debugger points to n->fired = true; as the problem?
void ParticleSystem::PrivProcessParticles(pNodePtr n, double frameTime)
{
while(n != NULL) {
n->fired = true;
if(!n->immortal)
n->life -= frameTime; //Decrement life
n->particle.ApplyAccel2D(frameTime);
/* Since the oldest particles will always be on
top of the queue, if life is zero, dequeue! */
if(n->life <= 0) {
if(head != NULL && !n->immortal) {
pNodePtr curr;
curr = head;
head = head->next;
delete curr;
}
}
n = n->next;
}
}
Allocation:
void ParticleSystem::AddParticle(double lifeIn, double x, double y, double angle,
double size, double force, bool immortalIn)
{
//Increment particle count
count++;
//Allocate
pNodePtr n = new particleNode;
//Initialize
n->particle.CreateQuad(size);
n->particle.SetTexture(texture);
n->particle.SetPos2D(x, y);
n->particle.SetRot2D(angle);
n->particle.SetTopSpeed(topSpeed);
n->particle.SetVelocity(force);
n->life = lifeIn;
n->immortal=immortalIn;
n->fired = false;
n->next = NULL;
//Place
if (head == NULL)
{
head = n;
tail = n;
n->next = NULL;
} else {
tail->next = n;
tail = n;
}
}
Node:
struct particleNode {
Quad particle;
double life;
bool fired;
bool immortal;
particleNode* next;
};
There's not enough information posted. However, here's one potential source of the problem.
When your PrivProcessParticles function performs its iterations over n, it can decide to delete head element of your list. But is it possible that at the moment when it decides to delete head, n is actually the same as head? If so, deleting head turns n into a dangling pointer, which leads to disastrous consequences at n = n->next.
Add assert(curr != n) before delete curr and see whether that assertion holds or fails.
Anyway, what is the starting value of n passed to PrivProcessParticles by the caller? Can it by any chance happen to be the same as head?
P.S. Also, just out of curiosity, the logic that you use to decide whether to perform the deletion or not seems to suggest that the decision is actually made about node n (you check n->life <= 0 and n->immortal). But then you proceed to delete head, not n... Is that by design?
P.P.S. A nitpick: you are doing excessive n->next = NULL initializations in your AddParticle.

Insertion Sort with a Singly Linked List in C++

I'm trying to write a method for my LinkedList class that will sort a linked list of Person objects by their name. My method compiles fine but when I try to sort a list of people, the output is incorrect. It also never stops running. For example, this code
Person *p1 = new Person("K", "B");
Person *p2 = new Person("A", "A");
Person *p3 = new Person("S", "M");
Person *p4 = new Person("B", "M");
LinkedList ll;
ll.insertFront(*p1);
ll.insertFront(*p2);
ll.insertFront(*p3);
LinkedList newList = ll.insertionSort();
newList.print();
cout << endl;
Gives this output
B, K
A, A
Could anyone help me figure out where I went wrong with my algorithm? Thanks!
This is the method I use to sort names by both first and last:
int Person::compareName(Person p)
{
if (lName.compare(p.lName) > 0)
{
return 1;
}
else if (lName.compare(p.lName) == 0)
{
if (fName.compare(p.fName) > 0)
{
return 1;
}
else return -1;
}
else return -1;
}
Insertion Sort Method:
LinkedList LinkedList::insertionSort()
{
//create the new list
LinkedList newList;
newList.front = front;
Node *n;
Node *current = front;
Node *trail = NULL;
for(n=front->link; n!= NULL; n = n->link)//cycle through old chain
{
Node* newNode = n;
//cycle through new, sorted chain to find insertion point
for(current = newList.front; current != NULL; current = current->link)
{
//needs to go in the front
if(current->per.compareName(n->per) < 0)
{
break;
}
else
{
trail = current;
}
}
//if it needs to be added to the front of the chain
if(current == front)
{
newNode->link = newList.front;
newList.front = newNode;
}
//else goes in middle or at the end
else{
newNode->link = current;
trail->link = newNode;
}
return newList;
}
You have current->link in your inner for loop, and in the else to the inner for loop. I assume that you really have current = current->link in the for loop or it does nothing. If so, you'd be skipping every other element.
You also have a language thing- you aren't creating new nodes, you're altering the nodes on your original list. That measn you're changing the list as you walk it, which will corrupt the list as you sort it. Behavior is undefined and dependent on the order in which you add elements.
Even after you have fixed any linked list handling issues (which I haven't looked at), your compareName() function has a flaw - when comparing Person objects that have the same last name it may return from the function without providing a value (in the cases where Name.compare(p.fName) <= 0).
Getting an indeterminate result from the compare function will break pretty much any sort.
Since this is likely homework, I'll leave correcting the problem as an exercise.

Simple Linked List Implementation in C++

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;
}