I'm relatively new to C++ and I have an issue which I really do not understand. My code creates a linked list. It is actually longer than this, but I chopped it down for the purpose of this question.
When I run the code, it adds three nodes and then when it goes to delete the node with the URI b, it calls the delete operator and ends up deleting the node, but then it seems to go back to delete operator (when I step through it) and it kills my whole list.
#include <iostream>
#include <string>
#include <cstdlib>
#include <cstdio>
using namespace std;
class CLinkedList
{
protected:
class ip_uri_store
{
public:
string uri, ip;
ip_uri_store* next;
ip_uri_store(const string& URI, const string& IP) {uri = URI, ip = IP, next = NULL;}
};
typedef ip_uri_store* nodeAddress;
nodeAddress head;
void AddNode(const string&, const string&, nodeAddress);
void DeleteNode(const string&, nodeAddress, nodeAddress);
public:
CLinkedList() {head = NULL;}
void AddNode(const string& URI, const string& IP) {AddNode(URI, IP, head);}
void DeleteNode(const string& URI) {DeleteNode(URI, head, head);}
};
void CLinkedList::AddNode(const string& URI, const string& IP, nodeAddress node)
{
nodeAddress temp = new ip_uri_store(URI, IP);
temp->uri = URI;
temp->ip = IP;
temp->next = head;
head = temp;
}
void CLinkedList::DeleteNode(const string& URI, nodeAddress node, nodeAddress behindNode)
{
if(node)
{
if(!node->uri.compare(URI))
node == head ? head = head->next : behindNode->next = node->next;
else
DeleteNode(URI, node->next, node);
delete node;
}
}
int main(int argc, char* argv[])
{
CLinkedList lList;
lList.AddNode("a", "1");
lList.AddNode("b", "2");
lList.AddNode("c", "3");
lList.DeleteNode("b");
return 0;
}
You are calling delete node; even if the comparison fails (i.e. node->uri != URI).
if(!node->uri.compare(URI))
{
node == head ? head = head->next : behindNode->next = node->next;
delete node;
}
else
DeleteNode(URI, node->next, node);
Also, the condition seems to be inverted.
First, you should use std::list and avoid reinventing the world.
Anyway, if you are stuck to this implementation for some reason :
temp->uri = URI; and temp->ip = IP; in the AddNode method are useless because the members are already initialized in the constructor of ip_uri_store class.
the deletion of the head of the list occurs because the 'delete node' should only be done in the case node->uri.compare(URI) in the DeleteNode method.
Again, you should seriously consider using standard classes...
You are calling delete on all the nodes. It needs to be moved inside the conditional so you only delete the nodes that match the URI
if(!node->uri.compare(URI)) {
node == head ? head = head->next : behindNode->next = node->next;
delete node;
} else {
DeleteNode(URI, node->next, node);
}
Related
I am using a doubly linked list and I am trying to use the data that is passed by reference to insert a node before said data. I've used string* data = new string(s); to allocate memory however, when I try to use data I get an error.
#ifndef __DOUBLYLINKEDLIST_H__
#define __DOUBLYLINKEDLIST_H__
//
//
#include
#include
using namespace std;
class DoublyLinkedList {
public:
DoublyLinkedList();
~DoublyLinkedList();
void append (const string& s);
void insertBefore (const string& s);
void insertAfter (const string& s);
void remove (const string& s);
bool empty();
void begin();
void end();
bool next();
bool prev();
bool find(const string& s);
const std::string& getData() const;
private:
class Node
{
public:
Node();
Node(const string& data);
~Node();
Node* next;
Node* prev;
string* data;
};
Node* head;
Node* tail;
Node* current;
};
void DoublyLinkedList::insertBefore(const string& s)
{
Node* ptr = head;
string* data = new string(s);
if (head == NULL)
{
append(s);
return;
}
if (head == current)
{
//this is where I get an error...
this->data= new Node();
current->prev = head;
current = head;
return;
}
There is no reason to use a pointer to a string, which forces you to manage memory. Use a simple string instead.
But this is not the problem here. Here a local variable has the same name than a class member of a Node , and the member in node gets never initalized. Furtherthermore the DoublyLinkedList has itself no such member, so this->data is unknown. See my comments here:
void DoublyLinkedList::insertBefore(const string& s)
{
...
string* data = new string(s); // --> ok, but this is local variable
if (head == NULL)
{
append(s);
return; // ouch !!! memory leak !! data pointer is never freed
}
if (head == current)
{
//this is where I get an error...
this->data= new Node(); // --> 'this' is a DoublyLinkedList, not a Node
...
return;
}
Now this being said, is it possbile that you make a confusion between the DoublyLinkedList and the nodes it contains ? See here a start of correction, but you need to do more to handle the linking between the nodes:
void DoublyLinkedList::insertBefore(const string& s)
{
Node* ptr = head;
if (head == NULL)
{
append(s);
return;
}
if (head == current)
{
string* data = new string(s);
Node nd = new Node();
nd->data = data; // initialize node's pointer
nd->prev = ... // you need to link new node to the rest
nd->next = ...
... // and you need to update the previous and next node
return;
}
Now, as said in the first place, replace the pointer to string with a string. At least, you'll avoid leaking memory, shallow copies, and lots of other troubles. Then you can focus better on the real problems of a linked list data structure.
I am trying to create an insertAfter function where it accepts a value by reference and then inserts that value after the current node. I am unsure how to implement the code.
Below is the header file and what I have tried but it does not compile.
#include <iostream>
#include <string>
class DoublyLinkedList {
public:
DoublyLinkedList();
~DoublyLinkedList();
void append (const string& s);
void insertBefore (const string& s);
void insertAfter (const string& s);
void remove (const string& s);
bool empty();
void begin();
void end();
bool next();
bool prev();
bool find(const string& s);
const std::string& getData() const;
private:
class Node
{
public:
Node();
Node(const string& data);
~Node();
Node* next;
Node* prev;
string* data;
};
Node* head;
Node* tail;
Node* current;
};
void DoublyLinkedList::insertAfter(const string& s)
{
// Node *temp, *var;
//var=(Node *)malloc(sizeof(Node));
if(head == NULL)
{
append(s);
}
temp->data=current;
}
void DoublyLinkedList::append(const string& s)
{
//create a new Node
current = new Node(s);
if (this->empty())//check if it is empty or not
{
this->head = this->tail = current;
}else{
//append to tail
current->prev = tail;
tail->next = current;
tail = current;
}
}
#endif
-Create a new node and store the data passed in. Set the current pointer to head.
Node * newNode = new Node(s);
current=head;
-You must traverse over the LinkedList using a loop which checks the values of each node using 2 pointers, one which trails behind the one that checks the node values.
while(current->data != insertAfter-
>data)
{
current=current->next;}
-Once the proper node is found with current, the loop should stop. Set the new node's next pointer to the insertAfter's next pointer, and the prev pointer to the insertAfter node.
newNode->next = insertAfterNode->next;
newNode->prev = insertAfterNode;
-You then set current's next pointer to the node you'd like to insert.
current->next = newNode;
Also, be sure to add each potential case. If it is the tail that was inserted after, be sure to set the tail pointer to newNode and same if it is inserted at the head. After traversing with the loop, put:
if(tail->data==current->data)
{
tail = newNode;
}
if(head->data==current->data)
{
head=newNode;
}
Edit: because this is a doubly LinkedList, the prev pointer can be implemented in place of trailCurrent, which is not needed at all.
List.h:
#pragma once
#include <iostream>
#include <memory>
#include <initializer_list>
class List{
public:
List();
List(const std::initializer_list<int> &list);
~List();
int size() const;
void push_back(int val);
void pop_back();
void pop_front();
friend std::ostream &operator << (std::ostream &os, const List &l);
private:
void printNodes() const;
struct Node {
Node(int data) : data(data) {}
std::unique_ptr<Node> next;
Node *previous;
int data;
};
int len;
std::unique_ptr<Node> head;
Node *tail;
};
List.cpp
#include "List.h"
List::List() : len(0), head(nullptr), tail(nullptr){
}
List::List(const std::initializer_list<int> &list) : len(0), head(nullptr), tail(nullptr) {
for (auto &elem : list)
push_back(elem);
}
List::~List() {
}
void List::push_back(int val){
if (tail == nullptr) {
head = std::make_unique<Node>(val);
tail = head.get();
head->next = nullptr;
head->previous = nullptr;
len++;
}
else {
tail->next = std::make_unique<Node>(val);
(tail->next)->previous = tail;
tail = tail->next.get();
tail->next = nullptr;
len++;
}
}
void List::pop_back(){
if(len == 1){
auto node = head.release();
delete node;
head = nullptr;
tail = nullptr;
}else{
// tail->previous;
}
}
void List::pop_front(){
if(len == 1){
auto node = head.release();
delete node;
head = nullptr;
tail = nullptr;
}else{
}
}
void List::printNodes() const{
Node *temp = head.get();
while (temp != nullptr) {
std::cout << temp->data << "\n";
temp = (temp->next).get();
}
}
int List::size() const{
return len;
}
std::ostream &operator<<(std::ostream & os, const List & l){
l.printNodes();
return os;
}
Source.cpp
#include <iostream>
#include "List.h"
using namespace std;
int main() {
List l{3, 5, 1, 6, 7};
cout << l << endl;
}
Hello Stack Overflow, I'm a Data structures student, and as practice, I'm trying to recreate std::list using smart pointers. Based on what I have read, it appears that unique_ptr should be the default one to use, with shared_ptr and weak_ptr only being used where unique_ptr cannot due to speed differences. Unfortunately, I have hit a wall when trying to implement pop_back() and pop_front(). Do I have to adopt shared pointers to complete the entire std::list reimplementation, or is there a way these functions can be done using unique pointers?
Yes, this is perfectly possible. Let's start with pop_back:
You have a pointer to the node already with tail, but that's not the interesting one, since it's just a raw pointer. The pointer that you need is the unique_ptr to that same node. Where is that pointer stored? Is there an easy way to get to it starting from tail?
Once you have the unique_ptr, unchaining the node from the list is as easy as resetting that pointer. Note that there is no need to call release and delete the node manually.
Now for pop_front: Here you already have the unique_ptr in hand, it's head. But you have to be careful as the whole list rests on this one. Resetting the head will make the entire list disappear. So be sure to detach the rest of the list from the head and reattach it with the list first. If you do this properly, deleting the original head will not even be a worry for you. Try it out!
Be sure to draw a picture of the list to visualize which node points where. It's rather difficult to keep all of this information in your head at once.
Suppose I have the following definition of List and Node:
template <class T>
class List {
public:
class Iterator;
class ConstIterator;
//Constructors and Destructors.
List() : head(NULL), tail(NULL), size(0) {}
List(const List& list);
~List();
//Methods
Iterator begin();
ConstIterator begin() const;
Iterator end();
ConstIterator end() const;
void insert(const T& data);
void insert(const T& data, const Iterator& iterator);
void remove(const Iterator& iterator);
int getSize() const;
Iterator find(const T& item);
ConstIterator find(const T& item) const;
void sort();
//Operators
List operator = (const List& list);
private:
class Node;
Node* head;
Node* tail;
int size;
};
template <class T>
class List<T>::Node
{
public:
//Constructors and destructors
Node(const T& _data, const Node* _next) : data(_data), next(_next) {}
~Node(); //Destructor
//Methods
//Operators
Node operator = (const Node& node);
private:
T data;
Node* next;
};
I'm writing a function to insert data into a list like this:
template<class T>
void List<T>::insert(const T& data)
{
Node newNode = new Node(data, NULL);
if (head == NULL)
{
head = &newNode;
tail = &newNode;
}
else
{
(*tail)->next = &newNode;
tail = &newNode;
}
size++;
}
However what I find strange is that if I swap (*tail)->next = &newNode; to (*tail).next = &newNode; it still compiles. Why, and what is the correct way of doing it?
The definitions of your classes can be (for the purposes of this question) simplified into:
class List {
...
private:
Node* head;
Node* tail;
};
class Node {
...
private:
Node* next;
};
Now in your List::insert method:
Node newNode = new Node(data, NULL);
(*tail)->next = &newNode;
...when you use new expression, the result will be pointer to the newly allocated memory.
What you should do is:
Node* newNode = new Node(data, NULL);
tail->next = newNode; // <-- equivalent to (*tail).next = newNode;
Using Node->tail is short form of writing (*Node).tail. Both forms are valid. Strangeus is the fact that you say that (*Node)->tail compiles. To this happens, Node must be defined as a double pointer, i.e.:
Node **tail;
But your code has some others bugs in. In this line:
Node newNode = new Node(data, NULL);
you are define a local object and assing a dynamic memory to it. The correct way is:
Node *newNode = new Node(data, NULL); // defining it as a pointer
and instead of assing as:
head = &newNode;
do:
head = newNode;
As a final note, consider using smart pointer instead of raw pointer. The former is safer than the last
The -> operator will automatically derefference a pointer for you then call the method to the right. So:
tail->next
would also work but
tail.next
wouldn't because tail is a pointer. To use the . operator you have to defrence the pointer first as in
(*tail).next
(*tail)
turns your pointer into an object. At that point you can use either -> or .
A . will not work on a pointer but -> will.
Generally, just for easy of typing I use -> because it is shorter then using (*) to turn a pointer into an object just so I can use a dot but they are equivalent operations.
You have noticed that (*tail)->next = &newNode and (*tail).next = &newNode both compile, which strikes you as odd.
But somehow you might also have noticed that this line also compiles!
Node newNode = new Node(data, NULL);
That is the thing that you should give you pause.
You are inside of a template here. Lots of things "compile".
Did you try instantiating the template?
ADDENDUM:
Here just to show you how crazy things can be, check out this program:
#include <iostream>
using namespace std;
template <class T>
class C {
void f();
};
template <class T>
void C<T>::f() {
int x = new int;
}
int main() {
std::cout << "Hello, world\n";
}
Now check this out:
$ g++ template-example.cpp && ./a.out
Hello, world
But now notice
#include <iostream>
using namespace std;
int main() {
int x = new int;
std::cout << "Hello, world\n";
}
which yields:
$ g++ hello.cpp
hello.cpp: In function ‘int main()’:
hello.cpp:4: error: invalid conversion from ‘int*’ to ‘int’
TL;DR: WHEN YOU ARE IN A TEMPLATE, THINGS THAT SHOULD NOT COMPILE SOMETIMES "DO"! (They're not really compiling -- YET!)
I'm not very good at this, and I am a bit stuck making the copy constructor for a single linked list and the nodes that go with it.
Here is my header file:
#pragma once
#include <iostream>
using namespace std;
class Node
{
public:
int data;
Node* next;
Node()
{
next = NULL;
data = 0;
}
Node(const Node& copyNode); /*: data(copyNode.data), next(copyNode.next ? new Node(*copyNode.next) : NULL)*/
};
class SLLIntStorage
{
public:
Node* head;
Node* current;
Node* tail;
void Read(istream&);
void Write(ostream&);
void setReadSort(bool);
void sortOwn();
void print();
void mergeSort(Node**);
Node *merge(Node*, Node*);
void split(Node*, Node**, Node**);
bool _sortRead;
int numberOfInts;
SLLIntStorage(const SLLIntStorage& copying) //: head(copying.head ? new Node(*copying.head) : NULL)
{
}
SLLIntStorage(void);
~SLLIntStorage(void);
};
inline ostream& operator<< (ostream& out, SLLIntStorage& n)
{
n.Write(out);
return out;
}
inline istream& operator>> (istream& in, SLLIntStorage& s)
{
s.Read(in);
return in;
}
Could anyone give me a hand on understanding how this works and what I could do to create it? Thank you.
To copy a linked list, you must iterate the entire linked list and make a copy of each of the nodes, and append that to the new list. Remember that you don't just copy the pointers, but you must copy the entire Node structure and any data that needs copying as well (e.g. if the datas are pointers, you'll need to do deep copying on those too).
So here's an example copy constructor for your SLLIntStorage class:
SLLIntStorage(const SLLIntStorage& copying) : head(NULL)
{
Node* cur = copying.head;
Node* end = NULL;
while (cur)
{
Node* n = new Node;
n->data = cur->data;
if (!head) {
head = n;
end = head;
} else {
end->next = n;
end = n;
}
cur = cur->next;
}
}
Note that I didn't take into account the tail and current data members, etc. You'll have to account for those.
As it is homework, I will try to give the idea from which you can figure out what you need to do with the copy constructors.
Node(const Node& copyNode) : data(copyNode.data),
next(copyNode.next)
{
// ....
}
In the above snippet you are just actually making the next to point the location copyNode::next is pointing to. So, you run in to problems when any of the pointer deallocates the resource it is pointing to leaving the other dangling.
So, you should make the pointer next each instance to point to a location it independently holds. So, -
Node(const Node& copyNode) : data(copyNode.data),
next(new Node)
{
(*next) = *(copyNode.next) ;
// ....
}
Also read this thread which has an excellent explanation - Rule of Three