有可能是重复的问题:
请问有人能向我解释一下移动语义吗?
有人能给我提供一个好的来源或者在这里解释一下什么是移动语义吗?
有可能是重复的问题:
请问有人能向我解释一下移动语义吗?
有人能给我提供一个好的来源或者在这里解释一下什么是移动语义吗?
暂时不要考虑C++0x。移动语义是一种与语言无关的东西——C++0x仅提供了一种使用移动语义进行操作的标准方法。
移动语义定义了某些操作的行为。大多数情况下,它们与复制语义相对比,因此首先需要定义它们。
采用复制语义进行赋值的行为如下:
// Copy semantics
assert(b == c);
a = b;
assert(a == b && b == c);
即,a
最终等于 b
,而我们不改变 b
。
使用移动语义的赋值操作具有较弱的后置条件:
// Move semantics
assert(b == c);
move(a, b); // not C++0x
assert(a == c);
请注意,使用移动语义进行赋值后,不再保证 b 不发生变化。这是关键的区别。
移动语义的一个好处是,在某些情况下可以进行优化。考虑以下常规值类型:
struct A { T* x; };
A
的对象相等,当且仅当它们的 x
成员指向相等的值。bool operator==(const A& lhs, const A& rhs) { return *lhs.x == *rhs.x; }
假设我们定义一个对象A
,它拥有其x
成员的指针的独占所有权。
A::~A() { delete x; }
A::A(const A& rhs) : x(new T(rhs.x)) {}
A& A::operator=(const A& rhs) { if (this != &rhs) *x = *rhs.x; }
现在假设我们想要定义一个函数来交换两个A
对象。
我们可以用常规的复制语义来完成它。
void swap(A& a, A& b)
{
A t = a;
a = b;
b = t;
}
// Not C++0x
void move(A& lhs, A& rhs)
{
lhs.x = rhs.x;
rhs.x = nullptr;
}
rhs
的指针移动到lhs
,然后放弃rhs
对该指针的所有权(通过将其设置为null)。这应该可以解释为什么移动语义的较弱后置条件允许优化。void swap(A& a, A& b)
{
A t;
move(t, a);
move(a, b);
move(b, t);
}
移动语义的另一个优点是它允许您移动无法被复制的对象。一个主要的例子就是 std::auto_ptr
。
C++0x通过其右值引用特性实现了移动语义。具体来说,这种操作:
a = b;
当b
是右值引用(拼写为T&&
)时,具有移动语义,否则具有复制语义。当b
不是右值引用时,您可以使用std::move
函数(与我之前定义的move
不同)来强制使用移动语义:
a = std::move(b);
std::move
是一个简单的函数,它本质上将其参数转换为右值引用。请注意,表达式(例如函数调用)的结果自动成为右值引用,因此您可以在这些情况下利用移动语义而无需更改代码。
要定义移动优化,您需要定义移动构造函数和移动赋值运算符:
T::T(T&&);
T& operator=(T&&);
由于这些操作具有移动语义,您可以自由修改传递的参数(前提是保持对象处于可析构状态)。
基本上就是这样。请注意,rvalue引用也用于允许C++0x中的完美转发(由于特别制作的类型系统相互作用),但这与移动语义并没有真正相关,因此我没有在此处讨论它。
我阅读了一年的文本解释,但并没有完全理解有关r-value引用的所有内容,直到我观看了Scott Meyer的这个精彩演讲:http://skillsmatter.com/podcast/home/move-semanticsperfect-forwarding-and-rvalue-references
他以幽默慢条斯理的方式解释,让人可以理解每个过程中发生的事情。
我知道,时长1小时30分钟,但实际上,这是我去年听到的最好的解释。
在阅读了文章(像其他答案一样)之后,观看这个视频将它们融合在我的脑海中,使我能够连贯地向同事解释如何使用std::unique_ptr(因为它仅允许移动语义,而不是复制),需要理解std::move(),它又需要理解移动语义。
boost::noncopyable
和std::auto_ptr
。public
拷贝构造函数声明,这意味着boost::noncopyable
对象与NRVO不兼容。
std::auto_ptr
是另一种绕过拷贝构造函数的尝试。你可能已经看到过它的“拷贝构造函数”的实现方式。template <typename _T>
auto_ptr(auto_ptr<_T>& source)
{
_ptr = source._ptr; // where _ptr is the pointer to the contained object
source._ptr = NULL;
}