[英]Move Semantics: Constructor and Assignment are not executed when performed std::move
#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.在这段代码中,我尝试连接使用 Vector 创建的2 个链表。
In main(), I called by using std::move l1.Concatenate(std::move(l2));
在 main() 中,我使用std::move
l1.Concatenate(std::move(l2));
调用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.因为最后,在 l1 与 l2 连接后,我希望
l2
变为 NULL 以防止调用析构函数时出现双重释放。 But, it seems like either move constructor or move assignment that I've created are not being executed.但是,我创建的移动构造函数或移动赋值似乎没有被执行。
Here's the thing:事情是这样的:
l1.Concatenate(l2)
by Reference void Doublyll::Concatenate(Doublyll& l2)
.void Doublyll::Concatenate(Doublyll& l2)
传递l1.Concatenate(l2)
void Doublyll::Concatenate(Doublyll& l2)
。 And it worked.void Doublyll::Concatenate(Doublyll&& l2)
there's this code I've comment: /*l2.head = l2.tail = nullptr;*/
which I've done manually to make l2
as NULL.void Doublyll::Concatenate(Doublyll&& l2)
看到这段代码,我已经注释了: /*l2.head = l2.tail = nullptr;*/
我已经手动完成将l2
设为 NULL。 It will worked perfectly and not cause double free.Doublyll l3(std::move(l2));
Doublyll l3(std::move(l2));
and like this l2 = std::move(l1);
l2 = 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));
那么,当我尝试像这样调用它时,为什么你认为它不起作用
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).
我认为这就像在使 l2 无法访问(NULL)之前将 l2 移动到 l1。
Is std::move doesn't work like that? std::move不是这样工作的吗? 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?
或者我的代码可能有问题,或者还有另一种方法仍然使用std::move来执行这样的功能? and maybe you can also explain me about this move semantics .
也许你也可以向我解释这个移动语义。 Thank You.
谢谢。
The code doesn't work because you commented out the part that makes it work, namely该代码不起作用,因为您注释掉了使其工作的部分,即
l2.head = l2.tail = nullptr;
You don't see the move constructor or move assignment operators being used because you have not written any code that uses them.您没有看到正在使用的移动构造函数或移动赋值运算符,因为您尚未编写任何使用它们的代码。 Did you expect something here to call them?
你期待这里有什么东西会打电话给他们吗?
I'm not sure what your question is, but I'm guessing you have a fundamental misunderstanding of what move semantics are.我不确定你的问题是什么,但我猜你对什么是移动语义有根本的误解。 The only thing that
std::move
does -- ever -- is indicate that something is allowed to be passed to a function that takes a T&&
as an argument. std::move
所做的唯一一件事——永远——表明允许将某些东西传递给一个将T&&
作为参数的函数。 Your responsibility when you write a function that takes a T&&
(aka an "rvalue reference") is to move the guts out of the thing you were passed and leave it in a state where all it can do is destruct without causing any problems.当您编写一个接受
T&&
(又名“右值引用”)的函数时,您的责任是将传递的内容移出并使其处于一种状态,在这种状态下,它只能进行破坏而不会造成任何问题。 This is precisely what you did when you manually set the head pointer to nullptr
.这正是您手动将头指针设置为
nullptr
时所做的。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.