C++ 为什么移动向量和移动元素对向量有不同的影响';s码?
我知道C++ 为什么移动向量和移动元素对向量有不同的影响';s码?,c++,vector,move,move-semantics,C++,Vector,Move,Move Semantics,我知道size()和empty()不需要先决条件,因此可以对moved from对象调用,并返回正确的结果。但我不明白结果背后的理论 std::vector<std::string> v = {"a", "b"}; std::string x = std::move(v[0]); std::cout << x << std::endl; // a std::cout << v.size() << std::endl;
size()
和empty()
不需要先决条件,因此可以对moved from对象调用,并返回正确的结果。但我不明白结果背后的理论
std::vector<std::string> v = {"a", "b"};
std::string x = std::move(v[0]);
std::cout << x << std::endl; // a
std::cout << v.size() << std::endl; // 2
std::cout << v.empty() << std::endl; // 0, false
auto y = std::move(v);
std::cout << v.size() << std::endl; // 0
std::cout << v.empty(); // 1, true
std::vector v={“a”,“b”};
std::string x=std::move(v[0]);
标准::cout
您不会将元素移出集合(在本例中为向量)。您正在将保存在v[0]
中的对象(一个std::string
实例)移动到另一个对象x
。集合本身没有更改
auto y = std::move(v);
这会将std::vector
对象v
移动到y
对象中。当移动对象时,您所做的是表示新项将对包含的任何数据或指针承担全部责任,并且旧项将具有不会影响新项的析构函数。目标公司有责任履行这一承诺
在向量中移动对象时,向量不知道对象已移动;因此大小不会改变;但是向量所持有的对象现在将是一个可以安全丢弃的“空白”项——例如,在一个唯一的\u ptr的情况下,它将是一个指向null \u ptr的ptr
移动向量会做完全相同的事情—它会将向量中的所有项移动到新项—然后清除自身,以确保在其析构函数上它不会影响它所持有的项。好的,我会尽力解释,如果您发现任何错误,请原谅我
首先,我们必须了解什么是std::move(…)
std::move(…)
获取一个对象并返回一个右值引用。就这样。现在,当创建一个新对象时,我们可以使用该rvalue引用在实际位置调用move构造函数
移动操作会发生。(廉价副本)
让我们看一个例子
#include <iostream>
#include <cstring>
#include <cstdlib>
using std::cout;
struct foo
{
int *resource;
foo()
: resource{new int[10]}
{
cout << "c'tor called\n";
}
foo(const foo &arg)
:foo{} // delegating constructors. calls above constructor first then execute my body.
{
/* Here expensive copy happens */
memcpy(resource, arg.resource, 10);
cout << "copy c'tor called\n";
}
foo(foo &&arg)
: resource{arg.resource} // just change ownership of resource.
{
/*
Here actual move happens.
Its implementator's job.
*/
cout << "move c'tor called\n";
arg.resource = nullptr;
}
};
int main()
{
foo a{};
foo b{a}; // calls copy constructor
/*
Do some stuff with a and b
*/
foo c{std::move(a)} // calls move constructor
}
确定通过调用移动构造函数创建新字符串对象。
在此操作之后,v[0]仍然存在,只是v[0]的内部常数发生了变化。所以v.size()是2
在此操作之后,通过调用move构造函数创建新的向量对象y
在向量的move构造函数中,实现者必须执行如下操作
arg.container_size=0;
因为vector的内容有了新的所有者 我想自己回答这个问题,因为我认为一些图表可以帮助人们(包括我在内)更容易地理解这个问题
这一切都归结于什么是真正的“移动”。假设我们有一个由3个字符串组成的向量
现在,如果我移动整个向量来表示另一个向量,那么堆上的所有内容都属于moved to vector对象,从moved from vector的角度看不到任何内容
显然,向量的大小变为0
如果我移动一个元素,在这种情况下是一个字符串,那么只有字符串的数据消失了。向量的数据缓冲区未被触及
向量的大小仍然是3
嗯,这是UB。这并不能解决这个问题,但您真的需要std::endl
所需要的额外内容吗<代码>'\n'
结束一行。如果你搬走了整栋房子,你就剩下一块空地,但如果有人搬出那栋房子里的公寓,公寓仍然在那里。@Vivick:你对一些类有额外的保证,如std::vector
。From“搬家后,其他的保证是空的()@codekaizer搬家的方法是把整个房子搬走;这比穿过每一个房间,把里面的每一件东西搬到马路对面的新房子里要快。然而,在这个类比中——这不像是搬家,更像是改变你的路名。。。所有东西都保持原样,但它只是被称为不同的东西移动时v
中的v[0]
发生了什么?v[0]
在移动v
之前和之后并不意味着相同的事情:别忘了v[0]
实际上是隐藏在sugar中的函数调用。@codekaizer related:@codekaizer您移动的是v
;它仍然有效。v[0]
引用的对象没有被移动(或者可能已经移动了-谁知道呢),但是v
是有效的,它是空的(这实际上是在标准的某处指定的)-导致v[0]
访问一个不存在的项。@UKMonkey没有那么清楚。它说的是一个移动向量的所有元素都被移动或破坏。当移动时,v
中的v[0]
会发生什么?从v
的角度看-什么都没有。容器不知道对其项所做的操作。@codekaizer您指的是哪一步?移动向量时,包含的std::string
实例不受影响。它们所在的内存将属于另一个向量,但这与字符串对象无关。当您将实际条目移动到其他地方时,它将执行通常的“获取不确定值”操作。
#include <iostream>
#include <cstring>
#include <cstdlib>
using std::cout;
struct foo
{
int *resource;
foo()
: resource{new int[10]}
{
cout << "c'tor called\n";
}
foo(const foo &arg)
:foo{} // delegating constructors. calls above constructor first then execute my body.
{
/* Here expensive copy happens */
memcpy(resource, arg.resource, 10);
cout << "copy c'tor called\n";
}
foo(foo &&arg)
: resource{arg.resource} // just change ownership of resource.
{
/*
Here actual move happens.
Its implementator's job.
*/
cout << "move c'tor called\n";
arg.resource = nullptr;
}
};
int main()
{
foo a{};
foo b{a}; // calls copy constructor
/*
Do some stuff with a and b
*/
foo c{std::move(a)} // calls move constructor
}
std::string x = std::move(v[0]);
auto y = std::move(v);