I need to make a linked list with classes. Each list will store two values: a URI and IP. After doing all the adding, I need to be able count the total number of items in the linked list. I have tried the following code but it doesn't compile. We are not allowed to use std::list. Any suggestions please?
#include <iostream>
#include <cstdlib>
#include <string>
using namespace std;
class ip_uri_store {
protected:
string ip;
string uri;
ip_uri_store *next;
public:
ip_uri_store(string huri, string hip);
void additem(string auri, string aip);
void deleteitem(string duri);
void findbyuri(string furi);
void findbyip(string fip);
int totalitems();
};
ip_uri_store *head = NULL;
ip_uri_store *curr = NULL;
void ip_uri_store::additem(string auri, string aip)
{
curr = head;
while (curr->next != NULL) {
curr = curr->next;
}
curr->uri = auri;
curr->next = new ip_uri_store;
curr->ip = aip;
curr->next = new ip_uri_store;
curr = curr->next;
curr = head;
}
int ip_uri_store::totalitems()
{
int i = 0;
curr = head;
while (curr->next != NULL) {
i += 1;
curr = curr->next;
}
return i;
}
int main(int argc, char *argv[])
{
if (argc == 1) {
cout << "123456, 123456#student.rmit.edu.au, Gordon Brown" << endl;
return (0);
}
head = new ip_uri_store;
curr = head;
int i;
for (i = 1; i < argc; i++) {
if (argv[i][0] == 'A') //add item
{
ip_uri_store.additem(argv[i + 1], argv[i + 2]);
i += 2;
}
else if (argv[i][0] == 'N') //print total tiems
{
cout << ip_uri_store.totalitems() << endl;
}
else {
cout << "command error\n";
return 0;
}
}
return (0);
}
Your ip_uri_store::additem() is pretty messed up. In it you change the curr object before you assign a new value to it:
curr->uri = auri;
curr->next = new ip_uri_store;
In doing so you change the last item in the list instead of assigning auri to the new item added later. (Interestingly, you got the order right with ip.)
I like the idea of giving pieces of code names, so that you can read what they do. Functions are what this is done with. For example, I'd factor out the code that finds the last list node
ip_uri_store *find_last_node(ip_uri_store *curr)
{
while (curr->next != NULL) {
curr = curr->next;
}
return curr;
}
and call it from ip_uri_store::additem():
void ip_uri_store::additem(string auri, string aip)
{
ip_uri_store *curr = find_last_node(head);
// ...
Now create a new object and remember its address in curr->next
// ...
curr->next = new ip_uri_store(auri,aip);
}
Your ip_uri_store::totalitems() returns an int. Why? Do you ever expect the count of objects to be negative? Better return an unsigned type.
You should consider what happens when you delete a list node. If it still points to a next object, chances are the pointer isn't stored anywhere else, and so the object (and those it points to) is (are) leaking. One way to deal with that is to write a destructor for ip_uri_store which deletes next. (If you want to delete a node without having it delete its own tail, you could assign NULL to its next pointer first.)
Also, according to the Rule of Three, you need to think about copying of list nodes. That's not easy to get right in the first try, so you might just want to forbid it:
class ip_uri_store {
private:
ip_uri_store(const ip_uri_store&); // not implemented
ip_uri_store& operator=(const ip_uri_store&); // not implemented
// ...
Instead of using global variables, you put them into class. That way you could have more than one list. Rename ip_uri_store to ip_uri_store_impl and pout it into a new ip_uri_store class:
class ip_uri_store {
private:
class ip_uri_store_impl { /* ... */ };
ip_uri_store_impl* head;
};
Since this class manages dynamically allocated objects, you need to think about destruction and copying such objects.
The wrapper class should have public methods that invoke the methods of ip_uri_store_impl whenthey nedd to. Functions like totalitems(), which operate on the whole list (instead of a node), should probably be implemented in the wrapper class.
You need to provide the two arguments to the constructor of your ip_uri_store class:
// The constructor call needs two arguments
curr->next = new ip_uri_store(huri, hip);
You cannot call instance methods on the class itself:
// Invalid, totalitems() is valid only on instances of ip_uri_store.
cout << ip_uri_store.totalitems() << endl;
Why are the variables head and curr global? They really should be data members of a class.
Pull out the ip, uri and next members of ip_uri_store and put them in their own structure, say ip_uri_store_node. Then, ip_ur_store_node can define a constructor that initializes them. Then make ip_uri_store hold the head and curr pointers to ip_uri_store_node instances.
This is what I mean:
struct ip_uri_store_node
{
string ip;
string uri;
ip_uri_store_node* next;
ip_uri_store_node(const char* u, const char* i)
: ip(i), uri(u), next(0) {}
};
class ip_uri_store
{
private:
ip_uri_store_node* head;
ip_uri_store_node* curr;
public:
// Initializes head and curr
ip_uri_store();
// These functions woud act on head and curr.
void additem(string auri, string aip);
void deleteitem(string duri);
void findbyuri(string furi);
void findbyip(string fip);
int totalitems();
};
int main()
{
ip_uri_store list;
// Do things with the list...
return 0;
}
The function additem can create new instances of ip_uri_store_node like this:
curr->next = new ip_uri_store_node(auri, aip);
The rest is up to you.
Related
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
The following is a basic code and I was wondering what was the basic way to write a loop on how to display the contents/data from the array of pointers. The top is a class with everything under public. Declaring a data of string followed by an array of pointers called next. In the main function, I'm declaring a few nodes and allocating memory to its followed by a string. A, B, and C. Towards the end of the code I'm connecting the pointers to each data and the last one C to NULL. And at the end, I'm having a bit of trouble writing or grasping the concept on how to write a loop to display it's contents, i.e Node1, Node2, Node3... Preferably a loop that'll display everything no matter the size.
#include <iostream>
using namespace std;
class node
{
public:
string data;
node * next;
};
int main()
{
node * A;
A = new node;
(*A).data = "node1";
node * B;
B = new node;
(*B).data = "node2";
node * C;
C = new node;
(*C).data = "node3";
(*A).next = B;
(*B).next = C;
(*C).next = NULL;
for(int i=0; *(next) != NULL; i++)
{
cout << *next[i[] << endl;
}
system("pause");
return 0;
}
use a temporary pointer that's initialized with the start of the node and use a while loop.
Node* tmp = A;
while (tmp) { // same as (tmp != NULL)
cout << tmp->data << endl;
tmp = tmp->next; // down the rabbit hole
}
Also, You could collapse the declaration of variables with assignment.
Node* A = new Node;
1.) remove for loop
printList(A);
void printList(node *first)
{
node *first = A;
while(first)
{
cout<<first->data<<endl;
}
}
This question already has answers here:
What are forward declarations in C++?
(8 answers)
Forward Declaration of Class, Function
(6 answers)
Closed 5 years ago.
My assignment is to create a general tree, and I decided to do this using three classes: node (contains character data, a pointer to a list ADT which contains all its children, and a pointer to another node for its own sibling nodes), list (contains all the children of a node), and the tree itself. The list is a singly linked list of nodes. The node class is supposed to be used in both the list and tree ADTs. Whenever I try to test my code in main() and see whether the classes are being created properly, I keep getting the error message "error: unknown type name 'list'". Can anyone look at my code and help me figure out how to correctly set up my classes?
//Genghis Khan
#include <iostream>
using namespace std;
class node //will be used for both list and trie ADTs
{
char character;
list* child; //all the children of the node will be stored in the list
node* sibling; //within a list, provides access to all children, functions like a "next"
friend class list;
friend class trie;
};
class list
{
private:
node* head;
node* tail;
public:
list()
{
head = new node;
tail = new node;
head->character = '*'; //head is a sentinel
tail->character = '*';
tail->sibling = head;
}
void insert(char a) //insertion into the list at the tail
{
node* v= new node;
tail->sibling = v;
v->character = a;
v->child = NULL;
tail = v;
}
int size() //so that I can use an empty() boolean
{
int size = 0;
node* temp = head;
if(temp->character != '*')
{
size += 1;
temp = temp->sibling;
}
else
{
temp = temp->sibling;
}
return size;
}
bool empty() //empty condition
{
return(size() == 0);
}
void print()
{
node* temp = head;
while(temp->character != '*' and temp != tail)
{
cout << temp->character;
temp = temp->sibling;
}
}
friend class trie;
friend class node;
};
class trie
{
private:
node* root;
public:
trie()
{
root = new node;
root->character = '*'; //sentinel
root->child = NULL; //to be changed when we add children
root->sibling = NULL; //will never change
}
bool haschildren(node* v)
{
return(v->child != NULL);
}
};
int main()
{
list a;
a.insert('G');
a.insert('e');
a.insert('n');
a.insert('g');
a.insert('h');
a.insert('i');
a.insert('s');
a.print();
}
As a note, I tried making "node" a struct instead of a class, but that did not help.
This is simple adding nodes to linked list. I'm not able to figure out why the head pointer is being set to null with every call to add function.
//struct declaration of node
struct node {
int data;
node* next;
};
//adding node to the head pointer
void add_node(node* head, int d)
{
node* temp = new node;
temp->data = d;
temp->next = NULL;
node* tmp = head;
if (tmp != NULL) {
cout << "shal";
while (tmp->next != NULL)
tmp = tmp->next;
tmp->next = temp;
}
else {
//cout<<temp->data;
head = temp;
}
cout << "dh" << head->data;
}
int main()
{
node* head = NULL;
// calling the add function
add_node(head, 10);
// head is being taken as null here
add_node(head, 20);
}
Output:
dh10nulldh20null
Please help me in understanding where it went wrong.
I guess you didn't get what a pointer is.
void plus_one(int num) {
num += 1;
}
int main() {
int num = 42;
plus_one(num);
std::cout << num << std::endl;
}
Obviously, num is still 42. Why? Because in function plus_one you get num by copy.
When you call your add_node, you send a copy of your head pointer. Since it is a pointer, you can modify what is POINTED BY the pointer, NOT the pointer itself. What you do is the same thing as trying to get 43 with my example... It's not possible if you are getting a copy.
You need to pass the address of your pointer, so call your function as it : add_node(&head, 10); and write your prototype as it : void add_node(node** head,int d). You will have to modify your function to fit with your new node**.
Why does it work? Because you modify the content of the pointer which is POINTING TO you original pointer (which is POINTING TO your structure).
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;
}