#include <iostream>
#include <utility>
#include <vector>
class Node
{
public:
int data;
Node* prev;
Node* next;
};
class Doublyll
{
private:
Node* head;
Node* tail;
public:
Doublyll();
Doublyll(std::vector<int> V);
Doublyll(const Doublyll& source);
Doublyll(Doublyll&& src) noexcept;
Doublyll& operator=(const Doublyll& rhs);
Doublyll& operator=(Doublyll&& src) noexcept;
~Doublyll();
friend std::ostream& operator<<(std::ostream& os, const Doublyll& src);
void Concatenate(Doublyll&& l2);
};
// Default Constructor will SET head and tail to nullptr
Doublyll::Doublyll()
: head(nullptr), tail(nullptr)
{
}
// Explicit Constructor using vector
Doublyll::Doublyll(std::vector<int> V)
: head(nullptr), tail(nullptr)
{
Node** p = &head;
for (auto& value : V)
{
Node* t = new Node;
t->data = value;
if (head == nullptr)
t->prev = nullptr;
else
t->prev = tail;
t->next = nullptr;
*p = t;
p = &(t->next);
tail = t;
}
}
// Copy Constructor
Doublyll::Doublyll(const Doublyll& source)
: head(nullptr), tail(nullptr)
{
std::cout << "Copy Construcor called!\n";
Node** p = &head;
// Iterate through all Node in source linked list, copying it to new object
for (Node* tmp = source.head; tmp != NULL; tmp = tmp->next)
{
Node* t = new Node;
t->data = tmp->data;
if (head == nullptr)
t->prev = nullptr;
else
t->prev = tail;
t->next = nullptr;
*p = t;
p = &(t->next);
tail = t;
}
}
// Move Constructor
Doublyll::Doublyll(Doublyll&& src) noexcept
: head(std::exchange(src.head, nullptr)), tail(std::exchange(src.tail, nullptr))
{
std::cout << "Move Constructor called!\n";
}
// Copy Assignment Operator
Doublyll& Doublyll::operator=(const Doublyll& rhs)
{
std::cout << "Copy Assignment Operator called!\n";
// Check self assignment
if (this != &rhs)
{
Doublyll tmp(rhs);
std::swap(tmp.head, head);
std::swap(tmp.tail, tail);
}
return *this;
}
// Move Assignment
Doublyll& Doublyll::operator=(Doublyll&& src) noexcept
{
std::cout << "Move Assignment called!\n";
if (this != &src)
{
std::swap(head, src.head);
std::swap(tail, src.tail);
}
return *this;
}
// Destructor
Doublyll::~Doublyll()
{
std::cout << "Desctructor called @ address " << &head << std::endl;
Node* p = head;
Node* tmp;
while (p != nullptr)
{
tmp = p;
p = p->next;
delete tmp;
}
}
// Display using Overloading << Operator
std::ostream& operator<<(std::ostream& os, const Doublyll& src)
{
Node* tmp = src.head;
if (tmp == NULL)
std::cout << "(EMPTY)\n";
else
{
for (; tmp != nullptr; tmp = tmp->next)
std::cout << tmp->data << " ";
std::cout << std::endl;
}
return os;
}
void Doublyll::Concatenate(Doublyll&& l2)
{
// Since we have tail, we can connect it by using it
tail->next = l2.head;
l2.head->prev = tail;
// Move first Node's tail to last Node of second linked list
tail = l2.tail;
// Make l2 as NULL
/*l2.head = l2.tail = nullptr;*/
}
int main()
{
// Create an Vector
std::vector<int> v1 = { 1, 3, 5, 7, 9, 11 };
std::vector<int> v2 = { 2, 4, 6, 8 };
// Create object and linked list
Doublyll l1(v1);
Doublyll l2(v2);
// Display linked list
std::cout << l1;
std::cout << l2;
// Concatenate 2 linked list
l1.Concatenate(std::move(l2));
// Display agaian after concatenate, l1 should connect with l2. and l2 should be EMPTY
std::cout << l1;
std::cout << l2;
std::cin.get();
}
In this code I tried to Concatenate 2 Linked List that I've created using Vector.
In main(), I called by using std::move l1.Concatenate(std::move(l2));
because at the end, after l1 connect with l2, I want l2
become NULL to prevent Double Free when Destructor is called. But, it seems like either move constructor or move assignment that I've created are not being executed.
Here's the thing:
- I know I don't need using std::move(l2), I can simply pass
l1.Concatenate(l2)
by Referencevoid Doublyll::Concatenate(Doublyll& l2)
. And it worked. But I want to practice using move constructor and assignment. - You can see in
void Doublyll::Concatenate(Doublyll&& l2)
there's this code I've comment:/*l2.head = l2.tail = nullptr;*/
which I've done manually to makel2
as NULL. It will worked perfectly and not cause double free. But, if I do std::move(l2) I don't need to done it manually, right? - My move constructor and move assignment is actually working if I moving old object to new object like this:
Doublyll l3(std::move(l2));
and like thisl2 = std::move(l1);
So, why do you think it's not working when I try to call it like this l1.Concatenate(std::move(l2));
to concatenate two linked list? I thought it's going to be like move l2 to l1 before make l2 inaccessible (NULL).
Is std::move doesn't work like that? or maybe there's something wrong in my code or there's another way that still using std::move to perform a function like that? and maybe you can also explain me about this move semantics. Thank You.