C++ - linked list - copy constructor - c++

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.

Related

Singly linked list with unique_ptr

I am trying to use smart pointers (std::unique_ptr) to create a singly linked list. Here is an example of a singly linked list with raw pointer.
struct Node {
int data;
Node *next = nullptr;
Node(int data) : data{data}, next{nullptr} {}
~Node() { std::cout << "Destroy node with data: " << data << '\n'; }
};
void print_list(Node *head) {
while (head != nullptr) {
cout << head->data << " --> ";
head = head->next;
}
cout << "nullptr" << std::endl;
}
void insert(Node *&head, int data) {
Node *new_node = new Node{data};
new_node->next = head;
head = new_node;
}
int main(int argc, char *argv[]) {
Node *head = nullptr;
for (int i = 0; i < 5; ++i) {
insert(head, i);
}
print_list(head);
return 0;
}
The output is:
4 --> 3 --> 2 --> 1 --> 0 --> nullptr
Apparently there is memory leak in the above code (destructor is not called). Now I want to use smart pointer to achieve the same thing:
struct Node {
int data = 0;
std::unique_ptr<Node> next;
Node(int data) : data{data}, next{nullptr} {}
~Node() { std::cout << "Destroy node with data: " << data << '\n'; }
};
void print_list(std::unique_ptr<Node> head) {
while (head != nullptr) {
std::cout << head->data << " --> ";
head = std::move(head->next);
}
std::cout << "nullptr" << std::endl;
}
void insert(std::unique_ptr<Node> &&head, int data) {
std::unique_ptr<Node> new_node{std::make_unique<Node>(data)};
new_node->next = std::move(head);
head = std::move(new_node);
}
// g++ -std=c++17 -Wall 2_1.cpp && ./a.out
int main(int argc, char *argv[]) {
std::unique_ptr<Node> head{nullptr};
for (int i = 0; i < 5; ++i) {
insert(std::move(head), i);
}
print_list(std::move(head));
return 0;
}
The output is:
4 --> Destroy node with data: 4
3 --> Destroy node with data: 3
2 --> Destroy node with data: 2
1 --> Destroy node with data: 1
0 --> Destroy node with data: 0
nullptr
We can observe that the life time of new_node ends when insert() returns. I would like to know if it's possible to use smart pointers to achieve singly linked list and retains the functions interface as above.
First thing, there is a problem with your print_list implementation(for both version for unique_ptr only). With your print_list, every time you assign head with a different uniq_ptr, you are actually deallocating the only Node in head, which is not desired. Instead, in your print_list, you should first create a temporary pointer pointing to head, then only iterate on the temporary pointer.
Now onto your unique_ptr version, you don't have to pass a unique_ptr as rvalue reference, you can also pass it as lvalue reference. Instead, your function signature would probably look like:
void print_list(const std::unique_ptr<Node>& head);
void insert(std::unique_ptr<Node> &head, int data);
This allow you to call them without using std::move in your main.
Now on to definitions. For your insertion, what you have is you first create a new Node with the given value, then you assign the old head to new node's next, and make the new node as the new head:
void insert(std::unique_ptr<Node> &head, int data)
{
// Use `auto` to avoid typing `....<Node>` twice
auto new_node = std::make_unique<Node>(data);
new_node->next = std::move(head);
head = std::move(new_node);
}
Alternatively, you can also add one more parameter to Node's constructor:
Node(int data, std::unique_ptr<Node>&& next = nullptr)
: data{data}, next{std::move(next)}
{}
Now you can simply create new_node like:
void insert(std::unique_ptr<Node> &head, int data)
{
// No need to assign `Node::next` separately
auto new_node = std::make_unique<Node>(data, std::move(head));
head = std::move(new_node);
}
Or even assign the new node to head directly:
void insert(std::unique_ptr<Node> &head, int data)
{
head = std::make_unique<Node>(data, std::move(head));
}
For print_list, we should first create a temporary pointer that points to the underlying object of head, then iterate the list by assigning the temporary pointer to its next object's underlying object:
void print_list(const std::unique_ptr<Node>& head)
{
// Create a pointing to the underlying object
// You can use `.get()` to get the underlying pointer
auto current = head.get();
// No need to explicit compare pointer types to `nullptr`
while (current) {
std::cout << current->data << " --> ";
// Make `current` point to the next underlying object
current = current->next.get();
}
std::cout << "nullptr" << std::endl;
}
Now your main would look like:
int main(int, char *[]) {
std::unique_ptr<Node> head;
for (int i = 0; i < 5; ++i) {
insert(head, i);
}
print_list(head);
return 0;
}
Demo

C++ class member: Why does class member return different value from second access?

I'm new to C++, and wrote NumberStack class in stack.cpp as follows, but the result is different from what I expected, So I need your help:
#include <iostream>
class LinkedListNode {
public:
int value;
LinkedListNode* next;
LinkedListNode(int initialValue) {
value = initialValue;
}
};
class NumberStack {
public:
LinkedListNode* head;
NumberStack(int initialValue) {
LinkedListNode node(initialValue);
node.next = NULL;
head = &node;
}
void push(int initialValue) {
LinkedListNode node(initialValue);
node.next = head;
head = &node;
}
int top() {
return head->value;
}
private:
};
int main() {
NumberStack myStack(6);
myStack.push(2);
myStack.push(5);
std::cout << myStack.top() << "\n";
std::cout << myStack.top() << "\n";
std::cout << myStack.top() << "\n";
return 0;
}
When executing this file, I got output like this:
$ g++ stack.cpp
$ ./a.out
5
45264732
45264732
I expected output would be like this.
5
5
5
So what caused this?
I'm using MacOS Big Sur
There are three changes that you need to make:
Initialize node's `next' in the constructor
Initialize head to nullptr
Allocate nodes dynamically
Here is how:
LinkedListNode(int v, LinkedListNode* n = nullptr): value(v), next(n) {}
Then call
head = new LinkedListNode(initialValue, head);
I would also give NumberStack a default constructor, rather than a constructor that takes the initial value.

Copying a Pointer to Allocated Memory

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;
};

C++: Copy constructor is deleting nodes in original object

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;
}
}

Why there's no error when I delete twice the same memory?

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.