我已经阅读了下面的文章,它对移动语义有了很好的了解:
但我仍然无法理解以下有关移动语义的事情 -
复制省略和 RVO 是否仍然适用于没有移动构造函数的类?
即使我们的类没有移动构造函数,但 STL 容器有一个。对于像这样的操作
std::vector<MyClass> vt = CreateMyClassVector();
并执行排序等操作。为什么 STL 不能在内部利用移动语义来使用复制省略或 RVO 等不需要移动构造函数的操作来改进此类操作?
3. 在下面的情况下,我们是否会从移动语义中受益 -
std::vector< int > vt1(1000000, 5); // Create and initialize 1 million entries with value 5
std::vector< int > vt2(std::move(vt1)); // move vt1 to vt2
由于整数是原始类型,移动整数元素不会提供任何优势。 或者在移动操作之后,vt2 只是指向堆中的 vt1 内存,并且 vt1 设置为 null。到底发生了什么?如果是后者,那么即使第 2 点也表明我们可能不需要为我们的类移动构造函数。
4. 当使用 std::move 在左值上调用 Push_back() 时,例如:
std::vector<MyClass> vt;
for(int i=0; i<10; ++i)
{
vt.push_back(MyClass());
}
MyClass obj;
vt.push_back(std::move(obj));
现在由于向量具有连续的内存分配,并且 obj 被定义在内存中的其他地方,移动语义如何将 obj 内存移动到向量 vt 连续内存区域,在这种情况下移动内存与复制内存一样好,如何移动通过简单地移动指向堆的不同区域中的内存的指针来证明向量连续内存需求的合理性。?
感谢您提前解释!
[最初发布为移动语义澄清,但现在随着上下文发生了一些变化,将其发布为新问题应尽快删除旧问题。]
复制省略和 RVO 仍然适用于没有移动构造函数的类吗?
是的,RVO 仍然起作用。实际上,编译器应该选择:
为什么 STL 不能在内部利用移动语义来使用复制省略或 RVO 等不需要移动构造函数的操作来改进此类操作?
STL 容器是可移动的,无论其中存储的类型如何。但是,对容器中的对象进行操作需要对象协作,因此,如果对象是可移动的,则“排序”(例如)只能移动对象。 在下面的情况下,我们是否会从移动语义中受益[...],因为整数是原始类型?
是的,你可以,因为容器是
可移动的,无论其内容如何。正如你所推断的,
st2
会窃取st1
的记忆。不过,移动后
st1
的状态未指定,所以我不能保证它的存储会被取消。当在左值上使用
push_back()
调用 std::move
时 [会发生什么]?
调用左值类型的移动构造函数,通常这涉及将原始值按位复制到目标,然后使原始值无效。一般来说,移动构造函数的成本与
sizeof(object)
成正比;例如,无论
sizeof(std::string)
有多少个字符,
std::string
都是稳定的,因为实际上这些字符存储在堆上(至少当它们有足够数量时),因此只有 pointer指向堆存储被移动(加上一些元数据)。
是的。
std::vector
struct MyClass
{
MyClass(MyClass&& other)
: xs(other.xs), size(other.size)
{
other.xs = nullptr;
}
MyClass(const MyClass& other)
: xs(new int[other.size]), size(other.size)
{
memcpy(xs, other.xs, size);
}
~MyClass()
{
delete[] xs;
}
int* xs;
int size;
}
使用移动构造函数,只需将
xs
和
size
复制到向量中(用于连续内存),但是我们不需要像复制构造函数中那样执行内存分配和
memcpy
。