I've been trying to rewrite some basic data structures using C++ to refresh my memory on some of the basics of OOP, but I've run into a silly problem already.
I'm trying to build a singly linked list, append the strings "Hello" and "World" to the list, and then view all of the contents within the list. This is a very easy task, but I'm getting a segmentation fault when I run the following code:
driver.cc
#include <iostream>
#include <string>
#include "SinglyLinkedList.h"
int main()
{
SLL<std::string> List;
List.Append("Hello");
List.Append("World");
List.visitAll(std::cout);
return 0;
}
Node.h
#ifndef NODE_H
#define NODE_H
template <class T>
class Node {
public:
Node<T>() {}
Node<T>(T init) { data = init; next = nullptr; }
void setData(T newData) { data = newData; }
void setNext(Node<T> *nextNode) { next = nextNode; }
const T getData() { return data; }
Node<T> *getNext() { return next; }
private:
T data;
Node<T> *next;
};
#endif
SinglyLinkedList.h
#ifndef SINGLY_LINKEDLIST_H
#define SINGLY_LINKEDLIST_H
#include "Node.h"
#include <iostream>
template <class T>
class SLL {
public:
SLL<T>() { head = nullptr; size = 0; }
~SLL<T>() {}
void Append(T added);
void Delete(T deleted);
void visitAll(std::ostream &outs);
private:
Node<T> *head;
long size;
};
template <class T>
void SLL<T>::Append(T added)
{
Node<T> *newNode = new Node<T>(added);
Node<T> *temp = head;
if(temp != nullptr) {
while(temp != nullptr) {
temp = temp->getNext();
}
temp->setNext(newNode); // seg fault here
}
else {
head = newNode;
}
}
template <class T>
void SLL<T>::visitAll(std::ostream &outs)
{
Node<T> *temp = head;
while(temp)
{
outs << temp->getData() << std::endl;
temp=temp->getNext();
}
}
#endif
Just debugging by hand, I create a new node with data = "Hello" and next = nullptr. This gets appended by the else in the void SLL<T>::Append method because temp == nullptr. However, on the second Append, the while loop runs once, then crashes when calling the setter of the Node class. I cannot figure out why this is the case.
I'm expecting to see
Hello
World
Am I being too tunnel-visioned? This is pretty silly. Sorry if it's too basic for SO...
Thanks,
erip
while(temp != nullptr) {
temp = temp->getNext();
}
temp->setNext(newNode); // seg fault here
That's because you are breaking out of the while loop when temp == nullptr.
Use:
while(temp->getNext() != nullptr) {
temp = temp->getNext();
}
temp->setNext(newNode);
Your while loop in Append ends with temp being a null pointer, therefore no temp->setNext()
Related
I am trying to make a linked list and test it in c++ using nodes. I create six nodes and then I print them forward and backwards like this:
main.cpp
#include "LinkedList.h"
#include <iostream>
#include <string>
#include <sstream>
using namespace std;
void TestAddHead();
int main()
{
TestAddHead();
system("pause");
return 0;
}
void TestAddHead()
{
cout << "Testing AddHead()" << endl;
LinkedList<int> data;
for (int i = 0; i < 12; i += 2)
data.AddHead(i);
cout << "Node count: " << data.NodeCount() << endl;
cout << "Print list forward:" << endl;
data.PrintForward();
cout << "Print list in reverse:" << endl;
data.PrintReverse();
}
LinkedList.h
#pragma once
#include <iostream>
#include <vector>
#include <array>
#include <stdexcept>
#include <string>
using namespace std;
template<typename T>
class LinkedList
{
public:
struct Node
{
T data_;
Node* next;
Node* previous;
};
void PrintForward() const;
void PrintReverse() const;
unsigned int NodeCount() const;
void AddHead(const T &data);
LinkedList();
LinkedList(const LinkedList<T> &list);
~LinkedList();
private:
Node* head = new Node;
Node* tail = new Node;
unsigned int count = 0;
};
template<typename T>
LinkedList<T>::LinkedList()
{
}
template<typename T>
LinkedList<T>::LinkedList(const LinkedList<T> &list)
{
}
template<typename T>
LinkedList<T>::~LinkedList()
{
}
template<typename T>
void LinkedList<T>::AddHead(const T &data)
{
Node* newNode = new Node;
newNode->data_ = data;
if (count == 0)
{
head = newNode;
tail = newNode;
head->next = nullptr;
head->previous = nullptr;
}
else
{
newNode->next = head;
head->previous = newNode;
head = newNode;
}
count = count + 1;
}
template<typename T>
void LinkedList<T>::PrintForward() const
{
Node* currentNode = head;
while (currentNode != nullptr)
{
cout << currentNode->data_ << endl;
currentNode = currentNode->next;
}
}
template<typename T>
void LinkedList<T>::PrintReverse() const
{
Node* currentNode2 = tail;
while (currentNode2 != nullptr)
{
cout << currentNode2->data_ << endl;
currentNode2 = currentNode2->previous;
}
}
template<typename T>
unsigned int LinkedList<T>::NodeCount() const
{
return count;
}
this should be the output of the program:
Testing AddHead()
Node count: 6
Print list forward:
10
8
6
4
2
0
Print list in reverse:
0
2
4
6
8
10
The program works and gives me the correct output but the problem is that it just crashes when it reaches the "10" at the bottom of the program and I don't know why, can anyone tell me why is this happening and a possible way to fix it? thank you
Your immediate problem, you never set the new node previous pointer to nullptr ( a problem that honestly should be rectified by a better constructed loop and/or a proper constructor for Node). Regardless, here...
template<typename T>
void LinkedList<T>::AddHead(const T &data)
{
Node* newNode = new Node;
newNode->data_ = data;
if (count == 0)
{
head = newNode;
tail = newNode;
head->next = nullptr;
head->previous = nullptr;
}
else
{
newNode->next = head;
newNode->previous = nullptr; // ADD THIS
head->previous = newNode;
head = newNode;
}
count = count + 1;
}
There are still several things wrong in this: memory leaks, empty copy-ctor and destructor, etc, but the above is the root of the current evil. That line could also be:
newNode->previous = head->previous;
but frankly that just confuses what you're doing. You're always landing your new nodes at the head of the list, so the previous member of said-same will always be nullptr (at least until you start studying circular lists).
i have recently started studying linkedlist in c++. Although i am finding it pretty confusing but i made some functions to insert and delete and element from the linkedlist.
The program worked fine with int data type. But i want to create a linkedlist of template type and i cannot understand how to do it.
I have tried to create a function to insert the node at the first position of link list but it gives me errors like access specifier needed before node etc ...
#include <iostream>
#include<conio.h>
using namespace std;
template<class T>
class node
{
public:
T data;
node *next;
};
template<class T>
class linklist
{
public:
node <int>*head;
linklist()
{
head = NULL;
}
bool Is_Empty(node <T>*ptr)
{
if(ptr==NULL)
{
return true;
}
return false;
}
void insert_at_head(T num)
{
if(Is_Empty(head))
{
node <T>*temp = new node; // error is on this line. it says access specifier needed before node.
temp->data = num;
head = temp;
temp->next = NULL;
}
else
{
node *temp = new node;
temp->data = num;
head = temp;
temp->next = head->next;
}
}
void show_list(node <T>*ptr)
{
cout<<"\nElements in the List are : ";
while(ptr!=NULL)
{
cout<<ptr->data<<" ";
ptr = ptr->next;
}
}
};
int main()
{
cout << "Hello world!" << endl;
getch()
}
Good evening. I have been trying to implement a Queue class in C++, taking a previously created Linked List class as a base.
Linked Linked List:
#include <cstddef>
#include <iostream>
#include <cstdio>
using namespace std;
template <class T>
class LinkedList {
public:
LinkedList() {
head = NULL;
}
~LinkedList() {
MakeEmpty();
}
struct Node {
T value;
Node *next;
};
Node* getHead() {
return head;
}
void Print();
void Insert();
void MakeEmpty();
private:
Node *head; // Head of the linked list.
};
Queue class:
#include "LinkedList.h"
template <class T>
class Queue {
public:
Queue() {
LinkedList<T>::Node *tnode = Q.getHead();
}
~Queue() {
Q.MakeEmpty();
}
void Enqueue( T x ) {
LinkedList<T>::Node *cnode = Q.getHead();
//Find the last element of Q
while( cnode -> next != NULL ) {
cnode = cnode -> next;
}
//Add x to the end of the queue
Q.Insert( x );
}
void Dequeue() {
LinkedList<T>::Node *hnode = Q.getHead();
//Rest of function
}
void Print() {
Q.PrintList();
}
private:
LinkedList<T> Q;
};
As you probably noticed, I am making them template classes. When compiling, I am told that tnode (found in the constructor of the Queue class) has not been declared in the scope. Any suggestions on how to fix this?
EDIT 1: The error message that I am getting is:
RCQueue.h: In constructor ‘Queue::Queue()’:
RCQueue.h:8:28: error: ‘tnode’ was not declared in this scope
LinkedList::Node *tnode = Q.getHead();
The main purpose of my constructor is to initialize the "head" pointer from the LinkedList class as NULL. I was also curious as to how one could go about declaring a variable of a structure that was declared in another template class.
Enqueue Algorithm :
1. Create a newNode with data and address.
2. if queue i.e front is empty
i. front = newnode;
ii. rear = newnode;
3. Else
i.rear->next = newnode;
ii.rear = newnode;
Dequeue Algorithm :
1. if queue is i.e front is NULL printf("\nQueue is Empty \n");
2. Else next element turn into front
i. struct node *temp = front ;
ii. front = front->next;
iii.free(temp);
C++ implementation :
#include <bits/stdc++.h>
using namespace std;
struct node
{
int data;
node *next;
};
node *front = NULL;
node *rear =NULL;
void Enque(int data)
{
node *newnode = new node;
newnode->data = data;
newnode ->next = NULL;
if(front==NULL)
{
front=newnode;
rear=newnode;
}
else
{
rear->next = newnode;
rear = newnode;
}
}
void Deque()
{
struct node *temp;
if (front == NULL)
{
printf("\nQueue is Empty \n");
return;
}
else
{
temp = front;
front = front->next;
if(front == NULL) rear = NULL;
free(temp);
}
}
void display()
{
node *temp=front;
if(front==NULL)
{
printf("\nQueue is Empty \n");
}
else
{
while(temp != NULL)
{
cout<<temp->data<<" ";
temp = temp->next;
}
}
cout<<endl;
}
You need typename in front of every use of the Node type in LinkedList that you reference within Queue because its dependent on the template parameter T. In specific,
template <class T>
class Queue {
public:
Queue() {
typename LinkedList<T>::Node *tnode = Q.getHead();
}
~Queue() {
Q.MakeEmpty();
}
void Enqueue( T x ) {
typename LinkedList<T>::Node *cnode = Q.getHead();
//Find the last element of Q
while( cnode -> next != NULL ) {
cnode = cnode -> next;
}
//Add x to the end of the queue
Q.Insert( x );
}
void Dequeue() {
typename LinkedList<T>::Node *hnode = Q.getHead();
//Rest of function
}
void Print() {
Q.PrintList();
}
private:
LinkedList<T> Q;
};
Notice the addition of typename before the uses of LinkedList<T>::Node.
Of course you'll also hear complaints about the missing definition of MakeEmpty() within LinkedList that is called in your Queue class, so just add a definition for it.
For more information on why typename is needed, this post explains its pretty clearly.
So far I have created a simple linked list project that I will build on in the future. All files compile correctly but when I try to build my project it comes up with a link error. I used an empty project to start. The main.cpp is used to demo that my linked list actually works. I'm really stuck and don't know how to solve this issue.
Main.cpp
#include <stdio.h>
#include <iostream>
#include <cstdlib>
#include "LinkedList.cpp"
#include "LinkedList.h"
using namespace SDI;
template <class T>
int LinkedList<T>::main()
{
LinkedList<T> menu;
menu.insert(1);
menu.insert(4);
menu.insert(7);
menu.insert(2);
menu.insert(8);
menu.display();
Std::cout << "-----" << endl;
menu.remove(2);
menu.remove(1);
menu.remove(10);
menu.display();
return 0;
};
header file LinkedList.h
#ifndef SDI_LL
#define SDI_LL
namespace SDI
{
template < class T >
class LinkedList
{
class Node
{
int number;
Node* next;
};
private:
T head;
T current;
T temp;
public:
LinkedList();
~LinkedList();
int main();
void insert(int add);
void remove(int remove);
void display();
};
}
#endif
LinkedList.cpp
#include <stdio.h>
#include <iostream>
#include <cstdlib>
#include <string>
#include "LinkedList.h"
using namespace SDI;
template <class T>
LinkedList<T>::LinkedList()
{
head = NULL;
current = NULL;
temp = NULL;
};
template <class T>
void LinkedList<T>::insert(int add)
{
newnode = new Node;
newnode->next = NULL;
newnode->number = add;
if (head != NULL)
{
current = head;
while (current->next != NULL)
{
current = current->next;
}
current->next = newnode;
}
else
{
head = newnode;
}
};
template <class T>
void LinkedList<T>::remove(int remove)
{
remove1 = NULL;
temp = head;
current = head;
while (current != NULL && current->add != remove)
{
temp = current;
current = current->next;
}
if (current == NULL)
{
std::cout << "N/A\n";
delete remove1;
}
else
{
remove1 = current;
current = current->next;
temp->next = current;
if (remove1 == head)
{
head = head->next;
temp = NULL;
}
delete remove1;
}
};
template <class T>
void LinkedList<T>::display()
{
current = head;
while (current != NULL)
{
std::cout << current->number;
current = current->next;
}
};
You need a global main() function in your program, not the static version from LinkedList. The main function is called the entry point, and a quick lookup of the error message tells you this.
http://en.cppreference.com/w/cpp/language/main_function
Something along the lines of this might work:
int main()
{
LinkedList<int> menu;
menu.insert(1);
menu.insert(4);
menu.insert(7);
menu.insert(2);
menu.insert(8);
menu.display();
Std::cout << "-----" << endl;
menu.remove(2);
menu.remove(1);
menu.remove(10);
menu.display();
return 0;
};
Currently, you define a completely unrelated "main" function inside your class. This is just a plain normal function of your class and in no way related to the previously mentioned int main() entry point function.
MySinglyLinkedList.h:
#include <iostream>
template<class T> class LinkedList;
template<class T>
class LinkedNode {
public:
LinkedNode(T new_data):data(new_data) {; }
private:
friend class LinkedList<T>;
LinkedNode<T> *next;
T data;
};
template<class T>
class LinkedList {
public:
LinkedList();
~LinkedList();
void PushNode(T new_data);
void Delete(LinkedNode<T> *pnode);
void Show();
private:
LinkedNode<T> *head; //Head pointer
LinkedNode<T> *tail; //Tail pointer
int length; //Length of the list
};
//Initialize an empty list when creating it
template<class T>
LinkedList<T>::LinkedList()
{
head = tail = NULL;
length = 0;
}
//delete all the nodes when deconstructing the object
template<class T>
LinkedList<T>::~LinkedList()
{
LinkedNode<T> *ptr = head;
while (ptr)
{
LinkedNode<T> *ptr_del = ptr;
ptr = ptr->next;
Delete(ptr_del);
}
}
//Add one node to the tail of the list
template<class T>
void LinkedList<T>::PushNode(T new_data)
{
LinkedNode<T> *pnew_node = new LinkedNode<T>(new_data);
pnew_node->next = NULL;
if (!length) {
head = tail = pnew_node;
length++;
} else {
tail->next = pnew_node;
tail = pnew_node;
length++;
}
}
//Delete the node pointed by pnode
template<class T>
void LinkedList<T>::Delete(LinkedNode<T> *pnode)
{
LinkedNode<T> *ptr = head;
if (pnode==head) {
head = pnode->next;
} else {
while(ptr->next != pnode)
{
ptr = ptr->next;
}
ptr->next = pnode->next;
}
if(pnode == tail)
tail = ptr;
delete pnode;
length--;
}
template<class T>
void LinkedList<T>::Show() //Print all the contents in the list
{
LinkedNode<T> *pnode = head;
while(pnode)
{
std::cout << pnode->data << std::endl;
pnode = pnode->next;
}
std::cout << "In total: " << length << std::endl;
}
The main function is as follows:
#include "MySinglyLinkedList.h"
#include <cstdlib>
#include <ctime>
using namespace std;
int main(int argc, char *argv[])
{
//list_len is the length of the list
int list_len = 5;
srand(time(0));
if (argc > 1)
list_len = atoi(argv[1]);
LinkedList<int> test_list; //Create the first list: test_list
for (int i = 0; i < list_len; i++)
{
//The elements in the list are random integers
int cur_data = rand()%list_len;
test_list.PushNode(cur_data);
}
test_list.Show();
LinkedList<int> test2 = test_list; //Create the second list: test2
test2.Show();
return 0;
}
Since I didn't define any copy constructor here, the default copy constructor will be called and do the bit copy when creating the second list, and as a result test_list and test2 will point to the same linked list. Therefore the first node of the list will be deleted twice when the two object are deconstructed. But the fact is nothing wrong happened when I compiled the program using GCC, and it ran successfully in Linux. I don't understand why no error occurred.
Deleting a pointer that has already been deleted causes undefined behavior, so anything can happen. If you want to make sure nothing happens, set the pointer to null after deletion. Deleting null does nothing, and it won't cause an error.
See: c++ delete (wikipedia)
As per Diego's answer, the C++ standard permits deleting NULL pointers. The compiler has no way to know what value your pointer will contain when you delete it the second time (i.e. it might be NULL), therefore it has no choice but to allow it in order to comply with the standard.