Linking a LinkedList as an element to a different LinkedList - c++

I'm working on a C++ assignment where I'll create a search engine on a linked list of linked lists. As per the requirements, I can't use other libraries nor STL.
Basically it will be like this (I removed the variables from small list since they are irrelevant):
My structs are these:
struct small
{
int data;
struct small *next;
};
struct big
{
int playerID;
string playerName;
string playerTeam;
struct small *goals;
struct big *next;
};
Here's the relevant code snippet, I think the problem is at addGoals(...) where I'm failing to assign the small element to the temp->goals.
class biglist
{
private:
big *head, *tail;
public:
biglist()
{
head = NULL;
tail = NULL;
}
. . .
void createbig(int ID, string name, string team)
{
big *temp = new big;
temp->playerID = ID;
temp->playerName = name;
temp->playerTeam = team;
temp->goals = NULL;
temp->next = NULL;
if (head == NULL)
{
head = temp;
tail = temp;
temp = NULL;
}
else
{
tail->next = temp;
tail = temp;
}
}
void addGoals(int id, small *s)
{
big *temp = head;
while (temp != NULL)
{
if (temp->playerID == id)
{
temp->goals = s;
break;
}
temp = temp->next;
}
}
void test()
{
big *temp = head;
while (temp != NULL)
{
if (temp->playerID == 1)
{
if (temp->goals !=NULL)
{
cout << temp->goals->data << endl;
}
else
{
cout << "goals null" << endl;
}
}
temp = temp->next;
}
}
}
. . .
 
class smalllist
{
private:
small *head, *tail;
public:
smalllist()
{
head = NULL;
tail = NULL;
}
void createsmall(int ID, biglist b)
{
small *temp = new small;
temp->data = ID;
temp->next = NULL;
if (head == NULL)
{
head = temp;
tail = temp;
temp = NULL;
}
else
{
tail->next = temp;
tail = temp;
}
b.addGoals(1, temp);
}
};
Finally, my main code:
int main()
{
biglist obj;
obj.createbig(1, "Player1", "Team1");
obj.createbig(2, "Player2", "Team2");
obj.displaybig();
smalllist sml;
sml.createsmall(9, obj);
sml.displaysmall();
obj.displaybig();
obj.test();
}
Debugging throws an exception at:
cout << temp->goals->data << endl;
saying that
Exception thrown: read access violation. temp->goals was nullptr.
I'm 90% sure I messed up something with pointers; but other stuff I've tried gave errors before compiling. I checked out some books / tutorials but couldn't figure it out.
Also if you have a better approach or saw one of the horrible mistakes that I'm making, please don't hold back :)
Thanks.
EDIT I changed my createbig() like this.
Currently it works with following codes:
void createbig(int ID, string name, string team, small *s)
{
big *temp = new big;
temp->playerID = ID;
temp->playerName = name;
temp->playerTeam = team;
temp->goals = s;
temp->next = NULL;
if (head == NULL)
{
head = temp;
tail = temp;
temp = NULL;
}
else
{
tail->next = temp;
tail = temp;
}
}
and added this to small
small getsmall(int i)
{
small *temp = head;
while (temp != NULL)
{
if (temp->data == i)
{
return *temp;
}
}
}
My final main function is
int main()
{
smalllist sml;
sml.createsmall(9);
sml.displaysmall();
biglist obj;
small s = sml.getsmall(9);
obj.createbig(1, "Player1", "Team1", &s);
//obj.createbig(2, "Player2", "Team2");
obj.displaybig();
obj.test();
}
While it ends successfully now, it gives the address of goals and I get this in debug section:

Let's look at what your code does, going through the main function. (Being able to walk through code like this is a useful skill. You can also use a debugger to help out, stepping through your function line-by-line.)
biglist obj;
Default construct a biglist. The head and tail are null. (By the way, nullptr is C++'s replacement for C's NULL.)
obj.createbig(1, "Player1", "Team1");
obj.createbig(2, "Player2", "Team2");
Add entries in obj for players with IDs 1 and 2. Their goals are null.
obj.displaybig();
Presumably an output of obj?
smalllist sml;
sml.createsmall(9);
sml.displaysmall();
These lines do something with a smalllist, but do not reference obj, so they are not relevant to this issue.
obj.displaybig();
Presumably an output of obj? Kind of redundant since nothing affected obj since the last display.
obj.test();
Call the test code, which finds the element for player ID 1 and outputs the data of that player's first goal. However, if you look up where that player was added, the goal is null, so you get a crash.
Separate from the above, there is probably some confusion in createsmall. Inside that function, a new biglist is created (not obj), and that list is told to add a goal to the player with ID 1. However, this has no effect the biglist in the main function.

You don't seem to have added any goals, so I'm assuming the code initializes with null.
and so the nullptr exception.
call addgoals() with the goals to player before test().
the other suggestions would be
to add a null check before printing goals
temp pointers need not be initialized with new big or small just the head of the list would be enough

Related

How do I fix this exception being thrown at a certain part of my code?

When I build the code, I don't get any errors in the output window. However, after running it, the compiler throws an exception (I'll comment where it is being thrown) at my code saying "Exception thrown: read access violation.
temp was 0xCDCDCDCD.".
I tried researching what this error is, and I found that this is for unassigned memory, but I don't see where something is being unassigned.
This is my Linked List .cpp file. The exception is thrown at a line towards the end of this file.
#include "linkedlist.h"
struct ll::node
{
weapons data;
node* next;
};
ll::ll()
{
head = NULL;
}
ll::~ll()
{
while (head != NULL)
{
node* temp = head;
head = head->next;
delete temp;
}
}
void ll::addItem(weapons obj)
{
node* newNode = new node;
node* temp = head;
newNode->data = obj;
if (head == NULL)
head = newNode;
return;
while (temp->next != NULL)
{
temp = temp->next;
}
if (temp->next == NULL)
{
temp->next = newNode;
return;
}
}
void ll::displayItems()
{
for (node* temp = head; temp != NULL; temp = temp->next)
{
temp->data.getDescription(); //EXCEPTION THROWN HERE
}
}
This file has the inherited class "Weapons" which is the object that is being called as "temp->data". As well as where I have "getDescription".
#include <vector>
using namespace std;
//base class
class inventory
{
protected:
//number of items in inventory
int mNumItems;
public:
//getters
void displayInv();
int getNumItems();
virtual void getDescription();
};
//weapon class
class weapons : public inventory
{
private:
//name of object
string mName;
//what the object is
string mInfo;
//how much of the object
int mAmount;
//how much damage does it do
double mDamage;
public:
//constructor
weapons();
weapons(string, string, double, int);
//getters
string getName();
void getDescription();
int getAmount();
double getDamage();
string getInfo();
//mutators
void setAmount(int);
};
This is where I define weapons
//weapon class
weapons::weapons()
{
mName = " ";
mInfo = " ";
mDamage = 0.0;
mAmount = 0;
}
weapons::weapons(string name, string info, double dmg, int amt)
{
mName = name;
mInfo = info;
mDamage = dmg;
mAmount = amt;
}
string weapons::getName()
{
return mName;
}
int weapons::getAmount()
{
return mAmount;
}
double weapons::getDamage()
{
return mDamage;
}
string weapons::getInfo()
{
return mInfo;
}
void weapons::getDescription()
{
cout << getName() << ", " << getDamage() << " damage, " << getInfo() << " Amount: " << getAmount() << endl;
}
void weapons::setAmount(int amt)
{
mAmount = amt;
}
Let me know if I need to include anymore files!
I get the expected results, which is for it to describe an item which I have in the Linked List. Unfortunately, my only problem is that this exception is being thrown.
Problem
In
struct ll::node
{
weapons data;
node* next;
};
and
void ll::addItem(weapons obj)
{
node* newNode = new node; // leaks if node not added
node* temp = head;
newNode->data = obj;
if (head == NULL)
head = newNode;
return; // this is a NASTY bug of a different sort. Most of the time
// the function will exit without doing ANYTHING
while (temp->next != NULL)
{
temp = temp->next;
}
if (temp->next == NULL) // the only way out of the above loop is if
// temp->next == NULL. This if is redundant.
{
temp->next = newNode;
return;
}
}
Nothing ever sets newNode->next to a safe value. That allows
while (temp->next != NULL)
{
temp = temp->next;
}
to fail because there are no guarantees that temp->next is ever NULL and the loop goes marching off the end of the list.
Solution
Force next to a safe value.
struct ll::node
{
weapons data;
node* next = NULL;
};
Or a more versatile version
struct ll::node
{
weapons data;
node* next;
node(const weapons & weap, // const reference eliminates a possible copy
node * link = NULL): // if you know what the next link will be,
// you can add it here. If not, it's always NULL
data(weap),
next(link)
{
}
};
addItem now looks something like
void ll::addItem(const weapons & obj)
{
if (head == NULL)
{
head = new node(obj); // only making node if we need it
// less chance of leak
}
else
{
node * temp = head;
while (temp->next != NULL)
{
temp = temp->next;
}
temp->next = newNode(obj);
}
}
But you can do something really sneaky here to make life easier. head is really a next pointer by another name, so if you can abstract the different name... And we can by tracking a pointer to next rather than a pointer to the node. This is really handy when you have to insert or remove an item: You have a reference both to the node in question and the insertion point in the previous node.
void ll::addItem(const weapons & obj)
{
node ** temp = &head; // get pointer to insertion point
while ((*temp) != NULL) // next node, including head, is not null
{
temp = &(*temp)->next; // get pointer to next insertion point
}
*temp = newNode(obj); // insert node
}
Half the code. Example of how this helps remove:
void ll::removeItem(const weapons & obj)
{
node ** temp = &head;
while ((*temp) != NULL && (*temp)->data != obj)
{
temp = &(*temp)->next;
}
if (*temp != NULL) // found it!
{
node * rem = *temp; // get node to remove so we don't lose it when we relink
*temp = rem->next; // point at item after rem
delete rem; // release item
}
}

insert function points to new data

Hi i am frankly new to these forums so i tried looking up as much as i could but i couldn't find anything relate-able to my problem.
I am trying to read nodes that i stored into a file and insert them into a linked list that i created
THE insert functions works perfectly fine however while loading it when i attempt to insert the node ,
1.it inserts the node
2.reads the new node
3.since im using a pointer , the pointer now points to the new read node
4.inserts the newly read node from the file into the list overwriting the old data.
This causes my old data to be completely lost and the new node to act as the header of the file
load function :
void load_file_students(linked_student &students)
{
node_student *test = new node_student;
ifstream stu_list("students.dat",ios::binary);
stu_list.read((char*)test, sizeof(*test));
while (!stu_list.eof())
{
//students.insert_node_list(test);
students.insert_node_list(test);
stu_list.read((char*)test, sizeof(*test));
}
stu_list.close();
}
the insert_node function:
void linked_student::insert_node_list(node_student *student)
{
node_student* temp = new node_student;
temp = student;
if (head == NULL)
{
head = temp;
}
else
{
node_student *ptr = this->head;
while (ptr->next != 0)
{
ptr = ptr->next;
}
temp->previous = ptr;
ptr->next= temp;
}
}
the node :
#pragma once
#include <string>
using namespace std;
static int roll_number = 1; // used for the personal identification of the student
class node_student
{
public:
bool fees_paid = true;
string name;
float CGPA;
int ID; // same as the static roll_number provided
int semester;
string department;
string elective_subjects[5];
node_student *next;
node_student *previous;
node_student();
~node_student();
};
during debugging mode it is evident that when i re-read the data in the load function during
stu_list.read((char*)test, sizeof(*test));
it overwrites the old data in the insert function as well even though it is called before this line.
Once again the insertion into the file works perfectly fine , i can actually see the values being loaded via the debugging mode
here is the insert function for reference
INSERT :
void update_student_file(linked_student testing)
{
node_student *temp = testing.head;
ofstream stu_list("students.dat",ios::binary);
while (temp != NULL)
{
stu_list.write((char*)temp, sizeof(*temp));
temp = temp->next;
}
stu_list.close();
}
Here a fix to load_file_students:
void load_file_students(linked_student &students)
{
node_student test; //way use dynamic allocation?
ifstream stu_list("students.dat",ios::binary);
while (stu_list.read((char*)test, sizeof(*test)))
{
students.insert_node_list(&test);
}
//file will be closed at the destructor.
}
Another fix for insert_node_list
void linked_student::insert_node_list(node_student *student)
{
node_student* temp = new node_student;
*temp = *student; //call copy constructor, do not copy pointer value.
if (head == NULL)
{
head = temp;
}
else
{
node_student *ptr = this->head;
while (ptr->next != 0)
{
ptr = ptr->next;
}
temp->previous = ptr;
ptr->next= temp;
}
}

Circular linked list: Infinite loop

I'm trying to make a circular link list but i'm facing with a problem.
If i run the program with those 2 lines of code above, when i compile and run, it gets an infinite loop of cin if the number of elements is higher than 2. Without them works fine but it isn't anymore a circular linked list. Can you help ?
The problem is right here:
toPush->next = head;
head->pred = toPush;
Full code:
#include <iostream>
using namespace std;
typedef int data;
// Nodes
struct elements {
data value;
elements* next;
elements* pred;
};
// Function that pushes the element to the end
void insertElementEnding(elements* &head, data var) {
elements* toPush = new elements;
toPush->value = var;
toPush->next = NULL;
toPush->pred = NULL;
if(head == NULL) {
head = toPush;
} else {
elements* node = new elements;
node = head;
while(node->next != NULL) {
node = node->next;
}
node->next = toPush;
toPush->pred = node;
toPush->next = head;
head->pred = toPush;
}
}
// Function that prints the list
void showList(elements* head, int numbers) {
for(int i = 0; i < numbers && head != NULL; i++) {
cout << head->value;
head = head->next;
}
}
int main() {
elements* head = NULL;
int var, n;
cout << "Introduce the number of elements: ";
cin >> n;
for(int i = 0; i < n; i++) {
cin >> var;
insertElementEnding(head, var);
}
showList(head, n);
return 0;
}
Thanks in advance.
You need to look for the start of the loop, not NULL, ie
while(node->next != NULL)
should be
while(node->next != head)
As a sidenote, you should use nullptr instead of NULL in C++.
Also you have a memory leak in your program. You dont need to allocate new memory just to get a pointer for iterating your list. This right here is the problem:
elements* node = new elements;
node = head;
A better way would just be
elements* node = head;
First, validation for NULL makes sense only to check if the list is not initialized, before inserting the first element in it.
For all other cases it is redundant as the head should always have previous and following elements for the circle. In case it is just one in the least, it points to itself.
Then if you change the function slightly, it will resolve the problem
void insertElementEnding(elements* &head, data var) {
elements* toPush = new elements;
toPush->value = var;
if(head == NULL) {
head = toPush;
head->next = toPush;
head->pred = toPush;
} else {
// insert the new element before the head
head->pred->next = toPush;
head->pred = toPush;
}
}

Counting occurrence in singly linked list by nodes

I am writing a simple app that gets a list and saves the objects as nodes in a singly linked list and we can add(), remove(), copy(), etc. each node depending on the given data set. each node has a char value which is our data and an int count which counts the occurrence of the related char.
e.g. for a list like
a, a, b, b, c, a
there would be three nodes (since there are three different characters) which are:
[a,3,*next] -> [b,2,*next] -> [c,1,*next] -> nullptr
bool isAvailable() checks if the data is already in the list or not.
Q: When inserting a data there are two options:
The data has not been entered: so we have to create a newNodewith the given data, count=1and *next=NULL.
The data is already entered: so we have to count++ the node that has the same data.
I know if the given data is available or not, but how can I point to the node with same data?
Here's the code:
#include "stdafx.h"
#include<iostream>
using namespace std;
class Snode
{
public:
char data;
int count;
Snode *next;
Snode(char d, int c)
{
data = d;
count = c;
next = NULL;
}
};
class set
{
private:
Snode *head;
public:
set()
{
head = NULL;
tail = NULL;
}
~set();
void insert(char value);
bool isAvailable(char value);
};
set::~set()
{
Snode *t = head;
while (t != NULL)
{
head = head->next;
delete t;
}
}
bool set::isAvailable(char value)
{
Snode *floatingNode = new Snode(char d, int c);
while(floatingNode != NULL)
{
return (value == floatingNode);
floatingNode->next = floatingNode;
}
}
void set::insert(char value)
{
Snode *newNode = new Snode(char d, int c);
data = value;
if (head == NULL)
{
newNode->next = NULL;
head = newNode;
newNode->count++;
}
else
{
if(isAvailable)
{
//IDK what should i do here +_+
}
else
{
tail->next= newNode;
newNode->next = NULL;
tail = newNode;
}
}
}
I know if the given data is available or not, but how can I point to the node with same data?
You'll need to start at the head of the list and iterate along the list by following the next pointers until you find the node with the same data value. Once you've done that, you have your pointer to the node with the same data.
Some other notes for you:
bool set::isAvailable(char value)
{
Snode *floatingNode = new Snode(char d, int c);
while(floatingNode != NULL)
{
return (value == floatingNode);
floatingNode->next = floatingNode;
}
}
Why is this function allocating a new Snode? There's no reason for it to do that, just initialize the floatingNode pointer to point to head instead.
This function always returns after looking at only the first node in the linked list -- which is not the behavior you want. Instead, it should return true only if (value == floatingNode); otherwise it should stay inside the while-loop so that it can go on to look at the subsequent nodes as well. Only after it drops out of the while-loop (because floatingNode finally becomes NULL) should it return false.
If you were to modify isAvailable() slightly so that instead of returning true or false, it returned either floatingPointer or NULL, you'd have your mechanism for finding a pointer to the node with the matching data.
e.g.:
// Should return either a pointer to the Snode with data==value,
// or NULL if no such Snode is present in the list
Snode * set::getNodeWithValueOrNullIfNotFound(char value) const
{
[...]
}
void set::insert(char value)
{
Snode * theNode = getNodeWithValueOrNullIfNotFound(value);
if (theNode != NULL)
{
theNode->count++;
}
else
{
[create a new Snode and insert it]
}
}
You had a lot of problems in your code, lets see what are they:
First of all, Snode doesn't need to be a class, rather you can go with a simple strcut; since we need everything public.(not a mistake, but good practice)
You could simple initialize count = 1 and next = nullptr, so that no need of initializing them throw constructor. The only element that need to be initialized through constructor is Snod's data.
Since c++11 you can use keyword nullptr instead of NULL, which denotes the pointer literal.
Member function bool set::isAvailable(char value) will not work as you think. Here you have unnecessarily created a new Snode and cheacking whether it points to nullptr which doesn't allow you to even enter the loop. BTW what you have written in the loop also wrong. What do you mean by return (value == floatingNode); ? floatingNode is a Snode by type; not a char.
Hear is the correct implementation. Since we don't wanna overwrite the head, will create a Node* pointer and assign head to it. Then iterate through list until you find a match. If not found, we will reach the end of the isAvailable() and return false.
inline bool isAvailable(const char& value)
{
Node *findPos = head;
while(findPos != nullptr)
{
if(findPos -> data == value) return true;
else findPos = findPos->next_node;
}
return false;
}
In void set::insert(char value), your logic is correct, but implementation is wrong. Following is the correct implementation.(Hope the comments will help you to understand.
void insert(const char& value)
{
if(head == nullptr) // first case
{
Node *newNode = new Node(value);
newNode->next_node = head;
head = newNode;
}
else if(isAvailable(value)) // if node available
{
Node *temp = head;
while(temp->data != value) // find the node
temp = temp->next_node;
temp->count += 1; // and count it by 1
}
else // all new nodes
{
Node *temp = head;
while(temp->next_node != nullptr) // to find the null point (end of list)
temp = temp->next_node;
temp = temp->next_node = new Node(value); // create a node and assign there
}
}
Your destructor will not delete all what you created. It will be UB, since your are deleting newly created Snode t ( i.e, Snode *t = head;). The correct implementation is as bellow.(un-comment the debugging msg to understand.)
~set()
{
Node* temp = head;
while( temp != nullptr )
{
Node* next = temp->next_node;
//std::cout << "deleting \t" << temp->data << std::endl;
delete temp;
temp = next;
}
head = nullptr;
}
Last but not least, the naming (set) what you have here and what the code exactly doing are both different. This looks more like a simple linked list with no duplicates. This is however okay, in order to play around with pointers and list.
To make the code or iteration more efficient, you could do something like follows. In the isAvailable(), in case of value match/ if you found a node, you could simply increment its count as well. Then in insert(), you can think of, if node is not available part.
Hope this was helpful. See a DEMO
#include <iostream>
// since you wanna have all of Node in public, declare as struct
struct Node
{
char data;
int count = 1;
Node* next_node = nullptr;
Node(const char& a) // create a constrcor which will initilize data
: data(a) {} // at the time of Node creation
};
class set
{
private:
Node *head; // need only head, if it's a simple list
public:
set() :head(nullptr) {} // constructor set it to nullptr
~set()
{
Node* temp = head;
while( temp != nullptr )
{
Node* next = temp->next_node;
//std::cout << "deleting \t" << temp->data << std::endl;
delete temp;
temp = next;
}
head = nullptr;
}
inline bool isAvailable(const char& value)
{
Node *findPos = head;
while(findPos != nullptr)
{
if(findPos -> data == value) return true;
else findPos = findPos->next_node;
}
return false;
}
void insert(const char& value)
{
if(head == nullptr) // first case
{
Node *newNode = new Node(value);
newNode->next_node = head;
head = newNode;
}
else if(isAvailable(value)) // if node available
{
Node *temp = head;
while(temp->data != value) // find the node
temp = temp->next_node;
temp->count += 1; // and count it by 1
}
else // all new nodes
{
Node *temp = head;
while(temp->next_node != nullptr) // to find the null point (end of list)
temp = temp->next_node;
temp = temp->next_node = new Node(value);
}
}
void print() const // just to print
{
Node *temp = head;
while(temp != nullptr)
{
std::cout << temp->data << " " << temp->count << "\n";
temp = temp->next_node;
}
}
};
int main()
{
::set mySet;
mySet.insert('a');
mySet.insert('a');
mySet.insert('b');
mySet.insert('b');
mySet.insert('c');
mySet.insert('a');
mySet.print();
return 0;
}

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.