我知道size()
和empty()
不需要前提条件,因此可以在移动的对象上调用并返回正确的结果。但我不明白结果背后的理论。
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
结果显示,如果移动元素,向量的大小将不会改变。但是如果你移动整个矢量,它就会变空。这是有道理的,但我觉得需要更多的解释,以便我将来可以处理类似的案件。
答案 0 :(得分:4)
std::string x = std::move(v[0]);
您没有将元素移出集合(在本例中为向量)。您正在将一个对象 - std::string
实例 - 保存在v[0]
中另一个对象x
。集合本身没有改变。
auto y = std::move(v);
这会将std::vector
对象v
移动到y
对象中。
答案 1 :(得分:2)
当你移动一个对象时,你正在做的是说新项目将对所包含的任何数据或指针负全部责任,并且旧项目将有一个不会影响新项目的析构函数。履行这一承诺是实现目标的责任。
在向量中移动对象的情况下,向量不知道对象已被移动;因此,大小不会改变;但是向量所持有的对象现在将是一个可以安全丢弃的“空白”项 - 例如,在unique_ptr的情况下,它将是一个指向null_ptr的ptr。
移动向量完全相同 - 它将向量中的所有项目移动到新的 - 然后清除自身以确保在其析构函数上它不会影响它所持有的项目。
答案 2 :(得分:0)
好的,我会尽力解释,如果你发现任何错误原谅我。
首先,我们必须了解std::move(...)
是什么?
std::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
}
这里我创建了foo a
对象,其中我用新的内存块初始化资源。没什么好看的。
在第二行中,通过传递对象b
作为参数来创建新对象a
。调用复制构造函数,因此对象b
与对象a
相同。
现在我想创建另一个与a
相同的对象,同时我知道我不再使用对象a
,所以我做foo c{std::move(a)}
。
我本可以做foo c{a}
,但据我所知,我不再使用a
,所以交换对象的内容效率很高。
在移动构造函数中,我需要确保执行此操作arg.resource = nullptr
。如果我不这样做,如果有人意外地改变了对象a
,这也会间接影响对象c
。
执行该对象后,a
仍然有效且存在。只有内容发生了变化。现在提出质疑
std::string x = std::move(v[0]);
通过调用move构造函数创建新的字符串对象。 在此操作之后,v [0]仍然存在,只有v [0]的内部构造已经改变。所以v.size()是2。
auto y = std::move(v);
在此操作之后,通过调用move构造函数创建新的矢量对象y
。
vector的内部移动构造函数,实现者必须做这样的事情 arg.container_size = 0; 因为vector的内容有新的拥有者。
答案 3 :(得分:0)