I have implemented a singly linked list using a template class and attempted to create a working copy constructor. When the copy constructor is called, it accurately duplicates the list. However, it deletes nodes from the original.
Here is the class implementation, the constructors, and the destructors:
template<class L>
class LinkedList
{
public:
LinkedList();
LinkedList(const LinkedList<L> &og);
~LinkedList();
Node<L>* AddToEnd(L object);
Node<L>* AddToMiddle(L object);
Node<L>* DeleteNode(L deleteItem);
Node<L>* ReverseList();
void Display();
int Size();
private:
Node<L>* head;
Node<L>* tail;
int size;
};
template<class L>
LinkedList<L>::LinkedList()
{
head = NULL;
tail = NULL;
size = 0;
}
template<class L>
LinkedList<L>::LinkedList(const LinkedList<L> &og)
{
cout << "\nCopy constructor has been called.\n\n";
head = new Node<L>;
*head = *og.head;
tail = new Node<L>;
*tail = *og.tail;
size = og.size;
}
template<class L>
LinkedList<L>::~LinkedList()
{
delete head;
delete tail;
}
And main.cpp:
#include "Header.h"
int main()
{
int int1 = 1;
int int2 = 2;
int int3 = 3;
int int4 = 4;
int int5 = 5;
cout << "Creating the first integer list..." << endl;
LinkedList<int> intList1;
intList1.AddToEnd(int1);
intList1.AddToEnd(int2);
intList1.AddToEnd(int3);
intList1.AddToEnd(int4);
intList1.AddToEnd(int5);
intList1.Display();
cout << endl << "Cloning and reversing..." << endl;
LinkedList<int> intList2(intList1);
intList2.ReverseList();
cout << "Original list: " << endl;
intList1.Display();
cout << "Reversed list: " << endl;
intList2.Display();
return 0;
}
The output looks like this:
Creating the first integer list...
1
2
3
4
5
Cloning and reversing...
Copy constructor has been called.
Original list:
1
2
1
Reversed list:
5
4
3
2
1
Your copy constructor is totally wrong. You should iterate through the linked list and copy each element of that list. Also your tail should point to the last element of the list.
So the code would be something like this.
template<class L>
LinkedList<L>::LinkedList(const LinkedList<L> &og)
{
cout << "\nCopy constructor has been called.\n\n";
head = new Node<L>;
*head = *og.head;
Node *p = og.head->next;
Node *i = head;
while (p != NULL) {
i->next = new Node<L>;
*(i->next) = *p;
p = p->next;
i = i->next;
}
tail = i;
size = og.size;
}
Your copy constructor definition needs to do more than what it is doing now.
Right now what you are doing is just duplicating the head and tail pointers, which is not enough. This way, your cloned list is essentially the same as first list. What you need to do is duplicate every node, something like:
for (Node<L>* n = og.first(); n != NULL ; n = n->next()) {
this->AddToEnd(n->value());
}
Additionally, your destructor is not cleaning up properly. You probably want to delete all the nodes you may have added.
Have your copy-constructor iterate through the nodes in og's list and append them to your list:
template<class L>
LinkedList<L>::LinkedList(const LinkedList<L> &og)
: head(NULL)
, tail(NULL)
{
for (Node* p(og.head); p != NULL; p = p->next)
{
AddToEnd(p->value);
}
}
Where value is the datum that a Node contains.
Your destructor is also incorrect. It should iterate through the list and delete the nodes one by one. Deleting only head and tail will cause the entire list to be lost if there are more elements:
template<class L>
LinkedList<L>::~LinkedList()
{
while (head)
{
Node<L>* temp(head);
head = head->next;
delete temp;
}
}
Related
I'm currently working on a class project where we make a linked list and we're supposed to create a function that clears the list then deletes it (with "delete LIST_NAME;"). I have implemented the function as instructed by my professor, also forcing the list to become null after the delete. The function works within itself, but when it returns to the main function, the list gets a new value.
Is this sort of function just not possible in C++?
#include <iostream>
struct Node
{
int val;
Node* next;
};
struct LinkedList
{
int count;
Node* head;
Node* tail;
};
void Clear(LinkedList* list) {
Node* node = list->head;
Node* next = nullptr;
while (node != nullptr) {
next = node->next;
delete node;
node = next;
}
list->head = nullptr;
list->tail = nullptr;
list->count = 0;
}
void Destroy (LinkedList* list) {
Clear(list);
delete list;
list = nullptr;
std::cout << "\n(should be) Destroyed";
}
int main() {
//creating a list element
Node* node = new Node;
node->val = 'a';
node->next = nullptr;
//inserting the element onto list
LinkedList* list = new LinkedList;
list->count = 0;
list->head = node;
list->tail = node;
std::cout << "\nList: " << list;
Destroy(list);
std::cout << "\nList: " << list;
std::cout << "\nEND";
}
This is just a snip of my code but it shows what I mean. Using the debugger the list has the value 0x0 by the end of the function but in the main function it's assigned a new value as shown by the debugger.
You take list by value so it's local to the function.
If you'd like to make changes to it that are visible at the call site, take it by reference:
// `list` is now a reference to the pointer at the call site:
void Destroy(LinkedList*& list) {
Clear(list);
delete list;
list = nullptr; // this now sets the referenced `LinkedList*` to `nullptr`
std::cout << "\n(should be) Destroyed";
}
I have one questions regarding searching elements on a Singly Linked List of ints, in this case, using C++. I'm creating my own version of list for exercising. This is the code
Let's suppose I have two search functions. I know we need to traverse the entire list until find the element because we don't have direct access like arrays.
The two functions are:
bool search(int n); // Traverse the list till find n.
bool search(Node* node, int n); Traverse the list till find n only after *node (included)
1 case: My list has the following elements: [0, 1, 2, 3]
If I search for 3 I easily find at the end of the list. Nice.
QUESTIONS:
2 case: My list has the following elements: [0, 1, 2, 3, 3, 3, 4, 5, 6]
If I search for 3 with:
bool search(int n);
I'm going to get the first 3 element always, except if I have a reference to the second or third 3 element to pass to that function:
bool search(Node* node, int n);
My questions is if that is the correct search algorithm in a singly linked list. The two types of functions or if I should have other types.
Bellow is the code for my actual code (I didn't put the code for searching):
SingleLinkedList.h
struct Node {
int data;
Node* next;
Node(int d = 0)
: data {d}, next {nullptr}
{}
};
class SinglyLinkedList {
public:
SinglyLinkedList();
~SinglyLinkedList();
void display();
bool addFirst(const int); // Add a node to the beginning of the list.
bool addFirst(Node*); // Add a node to the beginning of the list.
bool addLast(const int); // Add a node to the end of the list.
bool addLast(Node*); // Add a node to the end of the list.
private:
Node* head;
Node* tail;
};
SinglyLinkedList.h
#include "SinglyLinkedList.h"
#include <iostream>
SinglyLinkedList::SinglyLinkedList()
: head {nullptr}, tail {nullptr}
{}
SinglyLinkedList::~SinglyLinkedList() {
Node* iterationNode = head;
Node* actualNode {nullptr};
while (iterationNode != nullptr) {
actualNode = iterationNode;
iterationNode = iterationNode->next;
delete actualNode;
}
}
void SinglyLinkedList::display() {
std::cout << "################### Displaying Linked List ###################" << std::endl;
if (head == nullptr) {
std::cout << "Linked List is empty!" << std::endl;
}
else {
Node* iterationNode = head;
std::cout << "[ ";
while (iterationNode != nullptr) {
std::cout << iterationNode->data << " ";
iterationNode = iterationNode->next;
}
iterationNode = nullptr;
std::cout << "]" << std::endl;
}
std::cout << "##############################################################" << std::endl;
}
bool SinglyLinkedList::addFirst(const int n) {
Node* element = new Node {n};
if (head == nullptr) {
head = element;
tail = element;
}
else {
element->next = head;
head = element;
}
return true;
}
bool SinglyLinkedList::addFirst(Node* element) {
if (head == nullptr) {
head = element;
tail = element;
}
else {
element->next = head;
head = element;
}
return true;
}
bool SinglyLinkedList::addLast(const int n) {
Node* element = new Node {n};
if (head == nullptr) {
head = element;
tail = element;
}
else {
tail->next = element;
tail = element;
}
return true;
}
bool SinglyLinkedList::addLast(Node* element) {
if (head == nullptr) {
head = element;
tail = element;
}
else {
tail->next = element;
tail = element;
}
return true;
}
Program.cpp
#include <iostream>
#include "SinglyLinkedList.h"
int main() {
{
SinglyLinkedList list;
list.display();
list.addFirst(5);
list.addFirst(4);
list.addFirst(3);
Node* secondNode = new Node {2};
list.addFirst(secondNode);
Node* firstNode = new Node {1};
list.addFirst(firstNode);
Node* zeroNode = new Node;
list.addFirst(zeroNode);
list.addLast(6);
list.display();
}
system("pause");
}
Another question is, how can I protect my struct in a way the user of the program can not mess up changing the links/references directly. For example, in the Program.cpp, any programmer could simply do this:
secondNode->next = zeroNode
The answer to your first question depends on what you need. If you are doing this as a learning project, implement whatever you see fit. What you have described is appropriate for search by value.
The best way to prevent users from directly accessing your Node members in cases like this is to completely abstract the Node type away. You can do this simply by declaring and defining Node in your source file and use forward declarations of Node* in your header. Users who include your header will then not have any notion of your Node type whatsoever.
// SinglyLinkedList.h
class SinglyLinkedList {
//...//
struct Node* head; // head node is forward declared
//...//
}
// SinglyLinkedList.cc
struct Node {
//...
};
// define ll methods
If you do want the user to know about the Node type, one solution is to make its members private, create a public value accessor method, and make the Node a friend of the SinglyLinkedList class.
I am trying to implement copy constructor and I have strange problem.
Could you point what I am doing wrong?
I get:
double free or corruption (out): 0x08936a48
Wrong output:
Constructor called...
MyList:
10 --> 20 --> 30 --> 40 -->
MyList2:
10 --> 20 --> 30 --> 40 -->
Destructor called...
Code:
#include <iostream>
template <typename T>
class SingleLinkedList{
protected:
struct Node{
T data;
Node * next;
Node() : next(nullptr) {}
Node(const T num) : data(num), next(nullptr) {}
};
private:
Node * head;
Node * tail;
public:
SingleLinkedList(); // constructor
~SingleLinkedList(); // destructor
SingleLinkedList(const SingleLinkedList &object); // copy constructor
SingleLinkedList& operator=(const SingleLinkedList &object); // copy assignment
void insert(T const& value);
void displayList(std::ostream& stream = std::cout) const;
template <typename T>
SingleLinkedList<T>::SingleLinkedList() : head(nullptr), tail(nullptr){
std::cout << "Constructor called..." << std::endl;
}
template <typename T>
SingleLinkedList<T>::~SingleLinkedList(){
std::cout << "Destructor called..." << std::endl;
int index = 1;
Node * temp = nullptr;
while(head!=nullptr){
temp = head;
head = head->next;
delete temp;
//std::cout << "Node number: " << index << " destroyed" << std::endl;
index++;
}
}
template <typename T>
SingleLinkedList<T>::SingleLinkedList(const SingleLinkedList<T> &oldList){
SingleLinkedList<T> * newList = new SingleLinkedList<T>();
// is it necessary? my constructor by default initializes head and tail with nulls
head = nullptr;
tail = nullptr;
Node * temp = nullptr;
temp = oldList.head;
while(temp!=nullptr){
newList->insert(temp->data);
temp = temp->next;
}
}
template <typename T>
void SingleLinkedList<T>::insert(T const& value){
Node * temp = new Node(value);
//temp->data = value;
//temp->next = nullptr;
if(head==nullptr){
head = temp;
tail = temp;
}
else{
tail->next = temp;
tail = temp;
}
}
template <typename T>
void SingleLinkedList<T>::displayList(std::ostream& stream) const{
Node * temp = nullptr;
temp = head;
while(temp!=nullptr){
stream << temp->data << " --> ";
temp = temp->next;
}
stream << std::endl;
}
int main(){
SingleLinkedList<int> * myList = new SingleLinkedList<int>();
SingleLinkedList<int> * myList2 = myList;
myList->insert(10);
myList->insert(20);
myList->insert(30);
myList2->insert(40);
std::cout << "MyList:" << std::endl;
myList->displayList();
std::cout << "MyList2:" << std::endl;
myList2->displayList();
delete myList;
delete myList2;
return 0;
}
My "algorithm":
1. Create new list.
2. For each node of old list get data and insert it to new list.
I don't understand how using two different list I am deallocating the same part of memory.
I am a student, not pro. I am asking for your understanding.
Your copy ctor is really strange. Instead of adding elements to the list being constructed, you create a new list (why?) and then add elements to it. After that, you simply exit function. The new list created is left on the heap unreachable, i.e. leaked, and the list constructed remains empty.
Can you simply insert copied list's elements, not in a some newList, but in this one?
Double deletion happens for another reason: in your main you declare two pointers, myList and myList2, that both point to the same list in memory, and later try to delete them both. You can quickfix that by properly constructing myList2:
SingleLinkedList<int> * myList2{new SingleLinkedList<int>(*myList)};
but I suggest you get rid of pointers at all in your main:
SingleLinkedList<int> myList;
SingleLinkedList<int> myList2(myList);
(And don't forget to change all ->s into .s below afterwards.)
This is not Java after all, not every lifetime starts with new.
This is a general programming question, and I hope the answers will offer an alternative approach to the problem rather than a quick fix or hack. I have two objects, each of which has some pointers to allocated memory. I want to copy some internal information from one object to the other. Since the information is significantly large, I just want to copy the pointer. The problem is that when the destructor of the two objects are called, they each call the destructor on the internal information (which is now in both objects). This leads to the destructor being called twice on the same pointer.
Since this is quite a complex scenario, and it wouldn't be practical to show you the whole code. I have devised a simple example to illustrate the root of the problem. The code attaches two pre-existing lists without copying any data. As the output shows, the destructor gets called on the last two nodes multiple times. (Once as K is destroyed and again as L is destroyed, since both lists have a pointer to those nodes).
#include <iostream>
struct Node {
int data;
Node * next;
};
class List {
public:
List(const int);
~List();
void append(const int);
void append(const List&);
void print()const;
private:
Node * head;
Node * tail;
};
List::List(const int x)
{
Node * q = new Node;
q->data = x;
q->next = 0;
head = q;
tail = q;
}
List::~List()
{
while (head != 0){
Node * temp = head->next;
std::cout << "Deleting " << head->data << std::endl;
delete head;
head = temp;
}
}
void List::append(const int x)
{
Node * q = new Node;
q->data = x;
q->next = 0;
tail->next = q;
tail = q;
}
void List::append(const List& L2)
{
this->tail->next = L2.head;
this->tail = L2.tail;
}
void List::print()const
{
for (Node * iter = head; iter; iter=iter->next){
std::cout << iter->data << " ";
}
std::cout << std::endl;
}
int main()
{
List L = List(1);
L.append(3);
std::cout << "List L:\n";
L.print();
List K = List(5);
K.append(10);
std::cout << "List K:\n";
K.print();
L.append(K);
std::cout << "List L:\n";
L.print();
}
The output is:
List L:
1 3
List K:
5 10
List L:
1 3 5 10
Deleting 5
Deleting 10
Deleting 1
Deleting 3
Deleting 0
Deleting 39125056
Instead of using a raw pointer to your nodes, use std::shared_ptr<Node> and remove the explicit delete from your destructor. A shared pointer will keep the Node in memory as long as there is a shared_ptr instance pointing to it in scope. When there are no longer any shared_ptr instances pointing to a Node, the Node will be automatically deleted.
In the main function, you declared two local variable L, K, they will be deconstructed before program exists.
In your code, you're append the list K to list L, when deconstructing, K is deconstructed first, as the K still holds the pointer the Nodes 1,3, which means this nodes will be free. But the Node 3 in L still holds the pointer to K's head, that's how the error happens.
Put breakpoints in the deconstructors, you'll find out how it occurs.
Using std::shared_ptr instead of raw pointers will solve your problem.
Here's your code converted to use std::shared_ptr.
#include <iostream>
#include <memory>
struct Node {
int data;
std::shared_ptr<Node> next;
Node(int d) : data(d), next(nullptr) {}
};
class List {
public:
List(const int);
~List();
void append(const int);
void append(const List&);
void print()const;
private:
std::shared_ptr<Node> head;
std::shared_ptr<Node> tail;
};
List::List(const int x)
{
Node * q = new Node(x);
head.reset(q);
tail = head;
}
List::~List()
{
}
void List::append(const int x)
{
Node * q = new Node(x);
tail->next.reset(q);
tail = tail->next;
}
void List::append(const List& L2)
{
this->tail->next = L2.head;
this->tail = L2.tail;
}
void List::print()const
{
for (std::shared_ptr<Node> iter = head; iter.get() != nullptr; iter=iter->next){
std::cout << iter->data << " ";
}
std::cout << std::endl;
}
int main()
{
List L = List(1);
L.append(3);
std::cout << "List L:\n";
L.print();
List K = List(5);
K.append(10);
std::cout << "List K:\n";
K.print();
L.append(K);
std::cout << "List L:\n";
L.print();
}
The output:
List L:
1 3
List K:
5 10
List L:
1 3 5 10
Update
A bare bones implementation of the std::share_ptr functionality:
template <typename T>
struct SharedPtr
{
SharedPtr() : dataPtr(new Data(nullptr)) {}
SharedPtr(T* n): dataPtr(new Data(n)) {}
SharedPtr(SharedPtr const& copy) : dataPtr(copy.dataPtr)
{
dataPtr->useCount++;
}
~SharedPtr()
{
dataPtr->useCount--;
if ( dataPtr->useCount == 0 )
{
delete dataPtr;
}
}
void reset(T* n)
{
dataPtr->useCount--;
if ( dataPtr->useCount == 0 )
{
delete dataPtr;
}
dataPtr = new Data(n);
}
T* get() const
{
return dataPtr->n;
}
T* operator->() const
{
return get();
}
SharedPtr& operator=(SharedPtr const& rhs)
{
if ( this != & rhs )
{
dataPtr->useCount--;
if ( dataPtr->useCount == 0 )
{
delete dataPtr;
}
dataPtr = rhs.dataPtr;
dataPtr->useCount++;
}
return *this;
}
struct Data
{
Data(T* in) : n(in), useCount(1) {}
~Data() { if ( n != nullptr ) delete n; }
T* n;
size_t useCount;
};
Data* dataPtr;
};
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.