问题描述
我正在创建一个双向链表模板,我必须编写2个构造函数,然后在其中传递链表,然后构造一个具有相同数量的节点和相同数量的节点的新列表。节点的内存位置将是两者之间的唯一区别。
我有两个构造函数。一个是接受List&
的副本构造函数,接受List&&
的move构造函数:
List(const List &rhs);
List(List &&rhs);
现在我的问题是,因为这不是浅表副本,所以两者的代码相同,对吗?区别仅用于编译目的吗?我的理解是,我不需要std::move
来实现第二个构造函数。
第二个构造函数的目的是能够创建一个使用临时对象作为其参数的列表,例如:L1 = L2+ L3
,其中L2+L3
成为一个临时列表,一旦分配了该列表就将其删除到L1
。
解决方法
如果仅执行浅表复制,尤其是非指针值(或任何其他分配的资源)的浅表复制,则无需移动构造器。
,由于不是浅表副本,所以两者的代码相同,对吧?
List(const List &rhs)
副本构造函数应该深度复制(不是浅复制)数据(而不是节点本身)从rhs
到this
,但保持rhs
完整无缺。
List(List &&rhs)
移动构造函数应该从节点rhs
到this
移动(窃取)并离开{ {1}}处于空状态。
所以,不,他们不会使用相同的代码,甚至不会使用相同的代码。
与rhs
复制分配和移动分配运算符(通常通过copy-swap idiom利用复制/移动构造函数实现为了避免代码重复)。
例如:
operator=
像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;
}
*/
};
这样的操作必须实现L1 = L2 + L3
,该操作需要2个operator+
对象作为输入并返回带有 data 的新List
对象(而不是节点)同时从List
和L2
复制的{em> ,例如:
L3
返回的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;
}
将是一个临时对象,也就是一个右值,然后使用List
move assignment 运算符将其分配给L1
(或,operator=(List&&)
赋值运算符,使用{{1>}移动构造函数)。