the code is the same for both since it's not a shallow copy, right?
The List(const List &rhs)
copy constructor is expected to deep copy (not shallow copy) the data (not the nodes themselves) from rhs
to this
, but otherwise leave rhs
intact and untouched.
The List(List &&rhs)
move constructor is expected to move (steal) the nodes themselves from rhs
to this
and leave rhs
in an empty state.
So, no, they would not be using the same code, not even close.
Same thing with the operator=
copy assignment and move assignment operators (which are commonly implemented utilizing the copy/move constructors via the copy-swap idiom to avoid code duplication).
For example:
template<typename T>
class List
{
private:
struct node
{
T data;
node *previous;
node *next;
};
node *head = nullptr;
node *tail = nullptr;
size_t size = 0;
public:
// default constructor
List() = default;
// copy constructor
List(const List &rhs)
{
node **newNode = &head;
for(node *curNode = rhs.head; curNode; curNode = curNode->next)
{
*newNode = new node{curNode->data, tail, nullptr};
tail = *newNode;
++size;
newNode = &(tail->next);
}
}
// move constructor
List(List &&rhs)
: head(rhs.head), tail(rhs.tail), size(rhs.size)
{
rhs.head = rhs.tail = nullptr;
rhs.size = 0;
}
// destructor
~List()
{
node *curNode = head;
while (curNode) {
node *next = curNode->next;
delete curNode;
curNode = next;
}
}
// copy assignment
List& operator=(const List &rhs)
{
if (this != &rhs) {
List tmp(rhs);
std::swap(head, tmp.head);
}
return *this;
}
// move assignment
List& operator=(List &&rhs)
{
List tmp(std::move(rhs));
std::swap(head, tmp.head);
return *this;
}
/*
Alternatively, you can use just 1 implementation of operator=
for both copy and move assignments, by taking the input parameter
*by value* and letting the compiler decide which constructor
to call to initialize it, based on the type of input being assigned:
// copy/move assignment
List& operator=(List rhs)
{
List tmp(std::move(rhs));
std::swap(head, tmp.head);
return *this;
}
*/
};
An operation like L1 = L2 + L3
would have to implement operator+
that takes 2 List
objects as input and returns a new List
object with data (not nodes) that is copied from both L2
and L3
, eg:
template<typename T>
class List
{
...
public:
...
List& operator+=(const List &rhs)
{
if (rhs.head) {
List tmp(rhs);
node **ptr = (tail) ? &(tail->next) : &head;
*ptr = tmp.head; tmp.head = nullptr;
tail = tmp.tail; tmp.tail = nullptr;
size += tmp.size;
}
return *this;
}
// operator+ can be implemented either as a class member
// using *this as the left-hand operand...
List operator+(const List &rhs) const
{
List res(*this);
res += rhs;
return res;
}
};
// Or, operator+ can be implemented as a non-member function...
List operator+(const List &lhs, const List &rhs)
{
List res(lhs);
res += rhs;
return res;
}
The returned List
would be a temporary object, aka an rvalue, which would then be assigned to L1
using the operator=(List&&)
move assignment operator (or, the operator=(List)
assignment operator using the List(List&&)
move constructor).