I have been trying to work through this for the past couple of hours by going through my call stack, and have still yet to figure out what is going on!
My Sequence Database that basically gathers the info needed from a file, then calls on my Linked List class to create a new node with the gathered info, and put that node at the end of the Linked List:
Header:
#ifndef SEQUENCE_H
#define SEQUENCE_H
#include "DNA.h"
#include "DNAList.h"
class SequenceDatabase
{
public:
//Default Constructors
SequenceDatabase();
//Methods
void importEntries(string);
private:
DNAList list;
};
#endif
Source:
#include "SequenceDatabase.h"
#include "DNA.h"
#include "DNAList.h"
#include <fstream>
using namespace std;
SequenceDatabase::SequenceDatabase() //Default Constructor.
{
DNA object;
DNAList list;
}
void SequenceDatabase::importEntries(string name)
{
DNA* item;
ifstream file;
file.open(name);
if(!file.is_open())
{
cout << "Error opening file!" << endl;
exit(1);
}
char letter;
string label, sequence;
int ID, length, index;
file >> letter;
if(letter == 'D' || letter == 'd')
{
file >> label >> ID >> sequence >> length >> index;
DNA object(label,ID,sequence,length,index);
item = &object;
DNAList list(item);
}
}
My Linked List Header file:
class DNAList
{
public:
//Constructors
DNAList();
DNAList(DNA* newDNA);
//Linked List Functions
void push_back(DNA* newDNA);
DNA* findID(int ID);
void obliterate(int ID);
//DNANode struct for Linked List
struct DNANode
{
DNA* data;
DNANode* next;
DNANode* prev;
};
private:
DNANode* head;
typedef DNANode * ptr;
};
#endif
My Linked List class source file:
#include "DNAList.h"
#include "SequenceDatabase.h"
#include "DNA.h"
#include <iostream>
using namespace std;
DNAList::DNAList()
{
head = NULL;
}
DNAList::DNAList(DNA* newDNA)
{
cout <<"In DNA list second constructor" << endl;
ptr cur;
if(head == NULL)
{
ptr newNode = new DNANode;
cur = newNode;
cur -> data= newDNA;
head = cur;
cur -> prev = head;
cur -> next = NULL;
}
else
{
push_back(newDNA);
}
}
void DNAList::push_back(DNA* newDNA)
{
ptr cur;
ptr last;
cout << "Note: Adding " << newDNA -> getID() << " ..." << endl;
ptr newNode = new DNANode;
cur = head;
while(cur != NULL)
{
last = cur;
cur = cur -> next; //ERROR IS HAPPENING AT THIS LINE.
}
newNode -> data = newDNA;
newNode -> prev = last;
newNode -> next = NULL;
cur = newNode;
}
Now, I am new to using classes to contain my linked lists, so I am not sure if my node struct should be in public or private memory for my Linked List class, and also if my constructors are defined the way they should be. The Node struct is basically a pointer to the actual data that is in a header file called DNA with additional pointers to the struct for my next and previous Nodes so that the data is never messed with, but just pointed to.
The error is being stuck on the line right in the middle of my push_back function in my Linked List source file. I labeled it appropriately. Please can someone share some insight on what I am doing wrong here? Thanks!
My main file:
#include <iostream>
using namespace std;
#include "sequenceDatabase.h"
int main(){ //int argc, char* argv[]){
string commandsFileName;
commandsFileName = "lab1-commands-short.tab"; // for initial development
//commandsFileName = "lab1-commands.tab";
// commandsFileName = "lab1-commands-test.tab"; // for testing & grading
SequenceDatabase entries;
cout << "Importing " << commandsFileName << endl;
entries.importEntries(commandsFileName);
return 0;
}
if (...)
{
DNA object(label, ID, sequence, length, index);
item = &object;
DNAList list(item);
}
object is a locally-scoped object, meaning it is destroyed at the ending brace of the block. You're setting item to point to that local object and sending if off to the constructor of DNAList. The problem with that is if you try to access object after it has been destroyed, your program will no longer be in a valid state. This is because once your object is destroyed, the object that pointed to it will be left as a dangling pointer. Things like accessing a dangling pointer is known as Undefined Behavior.
But that's not where your UB comes from (yet). The real problem is in your DNAList constructor:
if (head == NULL)
{
// ...
}
else
{
push_back(newDNA);
}
head is an uninitialized pointer. Primitive objects (like int, char, char*) that are declared but not defined are uninitialized and thus have an indeterminate value. Testing objects in this state as if they had a value is also Undefined Behavior.
Note that UB can still appear to make your code work properly. But it can also do other nasty things that make no logical sense in your program.
head has the value of whatever was stored in the stack at that moment, which probably wasn't 0. So the condition fails and push_back() is invoked.
Then you do cur->next inside the while loop which dereferences an uninitialized pointer, finally causing an exception to be thrown.
That particular runtime error could be avoided by setting head to NULL inside the constructor body of DNAList and dynamically allocating object (so that it exists beyond the scope of the if statement, but there are still a few more things that are not right in your program as specified in the comments.
Related
i wanted to read the text file by words and then apply each word for the linked list
but when i apply the whole content will goes to the first node of linked list
any idea what do i have to modify in the code
updated
i wander how can i iterate the word over the linked list i know i need another loop inside the while but i do not how do to it
it is build with C++
the file is working it showing by words but what i do not understand is how to take the words to be linked to each other
code:
#include <bits/stdc++.h>
#include <iostream>
#include<string>
using namespace std;
class LinkedList{
// Struct inside the class LinkedList
// This is one node which is not needed by the caller. It is just
// for internal work.
struct Node {
string x;
Node *next;
};
// public member
public:
// constructor
LinkedList(){
head = NULL; // set head to NULL
}
// destructor
~LinkedList(){
Node *next = head;
while(next) { // iterate over all elements
Node *deleteMe = next;
next = next->next; // save pointer to the next element
delete deleteMe; // delete the current entry
}
}
// This prepends a new value at the beginning of the list
void addValue(string val){
Node *n = new Node(); // create new Node
n->x = val; // set value
n->next = head; // make the node point to the next node.
// If the list is empty, this is NULL, so the end of the list --> OK
head = n; // last but not least, make the head point at the new node.
}
// returns the first element in the list and deletes the Node.
// caution, no error-checking here!
string popValue(){
Node *n = head;
string ret = n->x;
head = head->next;
delete n;
return ret;
}
// private member
private:
Node *head; // this is the private member variable. It is just a pointer to the first Node
};
int main() { //linkedlist
LinkedList list;
//string usama="usama";
//list.addValue(usama);
//list.addValue("h");
//list.addValue("u");
//cout << list.popValue() << endl;
//cout << list.popValue() << endl;
//cout << list.popValue() << endl;
// because there is no error checking in popValue(), the following
// is undefined behavior. Probably the program will crash, because
// there are no more values in the list.
// cout << list.popValue() << endl;
//file
// filestream variable file
fstream file;
string word, t, q, filename;
// filename of the file
filename = "file.txt";
// opening file
file.open(filename.c_str());
// extracting words from the file
while (file >> word)
{
list.addValue(word);
cout<<list.popValue()<<endl;
// displaying content
//cout << word << endl;
}
return 0;
}
i know maybe something wrong with the while loop but i am stuck at it
You have several problems. The first is popValue() does not handle the case where head == nullptr. This will likely cause a segfault when you attempt string ret = n->x; You can add a check on head and initialize ret to avoid this issue (the empty ret will be used to terminate the iteration later)
string popValue(){
Node *n = head;
string ret {};
if (!head) /* validate head not nullptr */
return ret;
ret = n->x;
head = head->next;
delete n;
return ret;
}
Next, as addressed in the comment, you use 1 loop to addValue and popValue. That defeats the purpose of your list because every node you add is deleted when you popValue() leaving your list empty at the end of the loop. Use 2 loops, e.g.:
// extracting words from the file
while (file >> word)
list.addValue(word);
while ((t = list.popValue()).length())
cout << t << '\n';
(note: (t = list.popValue()).length() terminates the iteration when an empty-string is reached -- better to have popValue() return a node instead of string.
A short working example taking the filename to read as the first argument would be:
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
class LinkedList {
struct Node {
string x;
Node *next;
};
public:
LinkedList(){
head = NULL; // set head to NULL
}
~LinkedList(){
Node *next = head;
while(next) { // iterate over all elements
Node *deleteMe = next;
next = next->next; // save pointer to the next element
delete deleteMe; // delete the current entry
}
}
void addValue(string val){
Node *n = new Node(); // create new Node
n->x = val; // set value
n->next = head; // make the node point to the next node.
// If the list is empty, this is NULL, so the end of the list --> OK
head = n; // last but not least, make the head point at the new node.
}
string popValue(){
Node *n = head;
string ret {};
if (!head) /* validate head not nullptr */
return ret;
ret = n->x;
head = head->next;
delete n;
return ret;
}
private:
Node *head; // this is the private member variable. It is just a pointer to the first Node
};
int main (int argc, char **argv) { //linkedlist
LinkedList list;
// filestream variable file
fstream file;
string word, t, q, filename;
// opening file
if (argc < 2)
return 1;
file.open(argv[1]);
if (!file.is_open()) {
cerr << "file open failed.\n";
return 1;
}
// extracting words from the file
while (file >> word)
list.addValue(word);
while ((t = list.popValue()).length())
cout << t << '\n';
return 0;
}
Example Input File
$ cat dat/captnjack.txt
This is a tale
Of Captain Jack Sparrow
A Pirate So Brave
On the Seven Seas.
Example Use/Output
$ ./bin/llwords dat/captnjack.txt
Seas.
Seven
the
On
Brave
So
Pirate
A
Sparrow
Jack
Captain
Of
tale
a
is
This
Lastly see Why is “using namespace std;” considered bad practice? and Why should I not #include <bits/stdc++.h>?
I am practicing streams in C++ because I've got a midterm coming up in school. I am trying to write a small program that adds items to a list of Nodes (structs). I've got the following code, and am getting a clear error, but don't know how to fix it.
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
using namespace std;
struct Node {
string name;
Node *next;
};
void foo(istream &in, ostream &out, Node *list) {
string nom;
in >> nom; //Assigns value to nom via cin
Node *temp = list;
while (temp->next != NULL) { //Loops through list to find null pointer
temp = temp->next; //to add new Node to
}
Node item; //Creates new Node with a NULL next
item.name = nom;
item.next = NULL;
temp->next = item; //Adds item to the list
out << nom; //Outputs that it's been added
cout << " added" << endl;
}
int main() {
Node one;
one.next = NULL;
foo(cin, cout, &one);
}
The error I'm getting is:
Cannot convert 'Node' to 'Node*' in assignment (Line 22)
Aside from the type mismatch (Node and Node* are not the same), you did not allocate item dynamically, and it's going to be destroyed when foo finishes. Assigning &item would leave you with invalid temp->next.
In code:
Node *item = new Node;
item->name = nom;
item->next = nullptr; // I can't write NULL
temp->next = item;
condensed into one statement:
temp->next = new Node{nom, nullptr};
Im reading strings from a file and inserting them into a LinkedList in alphabetical order (C++). I have made the node and list classes but something is wrong with them. I have done this in Java and it works 100% without any problems. This leads me to believe that I must have messed up with pointers somewhere. This is also only the second time I use the '->' symbol. So I may have used it erroneously somewhere. Some helpful tips are appreciated. Thanks in advance.
//NODE CLASS
#include <string>
#include <iostream>
using namespace std;
class Node {
string word;
int count;
Node* next;
public:
Node (string aWord) {
word = aWord;
count = 1;
}
Node (string aWord, Node* theNext) {
word = aWord;
next = theNext;
}
void increaseCount() {
count++;
}
string getWord() {
return word;
}
int getCount() {
return count;
}
Node* getNext() {
return next;
}
void setNext(Node* theNext) {
next = theNext;
}
};
//LIST CLASS
#include<iostream>
using namespace std;
class LinkedList {
Node* head;
public:
LinkedList() {
head = new Node(" ");
}
void insert(string word) {
Node* temp = head;
Node* previous = head;
while (temp != NULL && temp->getWord() < word) {
previous = temp;
temp = temp->getNext();
}
if (temp == NULL) {
Node* node= new Node(word);
previous-> setNext(node);
} else {
if (temp-> getWord() == word) {
temp->increaseCount();
} else if (temp->getWord() > word) {
Node* node = new Node(word, temp);
previous->setNext(node);
}
}
}
void print() {
Node* temp = head->getNext();
while (temp != NULL) {
cout<< temp;
temp=temp->getNext();
}
}
};
//MAIN
#include <iostream>
#include <iostream>
#include <fstream>
#include "Node.h"
#include "LinkedList.h"
using namespace std;
int main(int argc, const char * argv[]) {
ifstream inFile("WordsStatisticData1.txt");
if (!inFile.is_open())
cout<< "Could not open the file"<< endl;
else {
string readData;
LinkedList list = *new LinkedList(); //Probably a problem here
while (inFile >> readData) {
list.insert(readData);
inFile.close();
list.print();
}
}
}
I may be declaring things totally wrong within the main as well.
My output looks like an address '0x' with random characters.
You're printing out temp where temp is a Node*. A pointer is just the address of an object, hence why you're getting an address in your output.
Seems like you want to get the string that the Node contains. If so, you want:
cout << temp->getWord();
Another problem you have is that you close your file and print the list inside the loop, which means it'll happen right after the first word has been read. You probably mean to do this after the loop, so all of the words in the file can be read.
You also have problem with the line you marked as such. Using the new keyword will dynamically allocate an object. These objects need to be later deleted with delete. However, you dereference the dynamically allocated object (with *) and copy it, losing any reference to the dynamically allocated object - this is a classic memory leak. The dynamic allocation here is completely unnecessary. Just do:
LinkedList list;
Here's the reference code:
#include <iostream>
using namespace std;
class linkedList {
struct listNode{ //a node of a list
int value;
struct listNode *next;
};
listNode *head;
public:
linkedList(){
cout << "hello1\n";
head = NULL;
};
linkedList(listNode* a){
cout << "hello2\n";
head = a;
};
~linkedList();
listNode* getHead() {return head;}
void appendNode(int);
//inline Search function due to unable to function outside of class definition
listNode* rangeSearch(int a, int b){
//listNode to search
listNode *search = head;
//listNode* toReturn = new listNode;
//listNode to return list of values that are found within range
linkedList *found = new linkedList;
while(search){
//if the current value is within range, then add to list
if(search->value >= a && search->value <= b){
//append searched value onto found
found->appendNode(search->value);
//after appending, go to next value
}
search = search->next;
}
return found->getHead();
}
void display();
};
int main()
{
cout << "Programmer : n\n";
cout << "Description : \n";
linkedList* list = new linkedList;
int x = 12;
//values to search
int s1 = 10, s2 = 14;
// adds 2 to each number on list for 5 times
for(int i = 0; i < 5; i++){
list->appendNode(x);
x += 2;
}
//create something to hold pointer of found to be deleted when done using
//print list
cout << "Original set of numbers in linked list: ";
list->display();
cout << "\nThe following are the values withing ranges: " << s1 << " and " << s2 << ":\n";
//EDITED:
//list->rangeSearch(s1,s2);
linkedList foundList(list->rangeSearch(s1,s2));
foundList.display();
//End of edit 6:40PM 7/18/13
cout << "\nHere are the original set of numbers in linked list (again): ";
list->display();
delete list;
return 0;
}
void linkedList::appendNode(int newValue)
{
listNode *newNode = new listNode(); // To point to a new node
listNode *nodePtr; // To move through the list
// Allocate a new node and store newValue there.
newNode->value = newValue;
newNode->next = 0;
// If there are no nodes in the list
// make newNode the first node.
if (!head)
head = newNode;
else // Otherwise, insert newNode at end.
{
// Initialize nodePtr to head of list.
nodePtr = head;
// Find the last node in the list.
while (nodePtr->next)
nodePtr = nodePtr->next;
// Insert newNode as the last node.
nodePtr->next = newNode;
}
}
void linkedList::display() {
for(listNode* p = head; p != NULL; p = p->next)
cout << p->value << ' ';
}
linkedList::~linkedList()
{
cout << "\ndestructor called";
listNode *nodePtr; // To traverse the list
listNode *nextNode; // To point to the next node
// Position nodePtr at the head of the list.
nodePtr = head;
// While nodePtr is not at the end of the list...
while (nodePtr != NULL)
{
// Save a pointer to the next node.
nextNode = nodePtr->next;
// Delete the current node.
delete nodePtr;
// Position nodePtr at the next node.
nodePtr = nextNode;
}
}
So a couple of questions here. First, why is it when I try to put the rangeSearch member function outside of the class definition, the compiler gives an error saying listNode* type is not recognized?
Second, this has to do with destructors. In this program, 2 instances (list & found list) were created but only 1 destructor was called. Can someone explain why? My intuition tells me that the dynamically allocated pointer to linkedList object did not get destructed. However, I don't know why. The reason I had to use dynamically allocated memory is primarily because I want to pass the pointer back to the main function. If I don't, when rangeSearch exits, the pointer will be passed back to main but whatever list the pointer had would be deconstructed after
return ptr; (assume ptr is a pointer to a linkedList declared in rangeSearch)
which will cause my program to crash because, now the address has nothing in it and I'm trying to call... nothing.
Well, as usual I would appreciate whoever the great Samaritan out there who would be more than willing to educate me more about this.
First, you are having an issue with scoping. In C++, the curly braces define a new scope, so you are defining listNode inside the class linkedlist. If you want to access it, you'd have to use the scoping operator as such linkedlist::listNode
I don't entirely understand your second question. I only see one call to delete, so why do you think two destructors will be called? The destructors are only called when you call delete, so unless you specify that you want to destroy it, it's still going to be there.
Although I don't entirely understand your question, I see that you returned a pointer to the head in rangeSearch, but you don't assign it to anything. What this means is that you will have a memory leak; you allocated memory for the found, but then don't do anything with it. Actually since you only return the head, you still wouldn't be able to delete it if you did assign something to it, because you wouldn't have access to linked list itself.
linkNode is nested inside of linkedList. Move listNode outside of the linkedList class, and you won't get the first error. Or you can use it's full declaration, linkedList::listNode. Also, if you leave linkNode nested, you will have to make it public.
In main, you can just say
linkedList list;
instead of
linkedList* list = new linkedList;
rangeSearch() is returning a value, but that value is never being assigned to anything in main(). rangeSearch() is allocating a linkedList, but it never gets deleted.
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.