Move semantics - what it's all about?
Possible Duplicate:
Can someone please explain move semantics to me?
号
有人能给我指出一个好的来源或者解释一下这里的移动语义是什么?
暂时忘记C++0X。移动语义是语言无关的东西——C++0X仅仅提供了一种用移动语义执行操作的标准方法。
定义移动语义定义特定操作的行为。大多数时候,它们与复制语义形成了对比,因此首先定义它们是很有用的。
具有复制语义的赋值具有以下行为:
1 2 3 4 | // Copy semantics assert(b == c); a = b; assert(a == b && b == c); |
也就是说,
带有移动语义的赋值具有较弱的后置条件:
1 2 3 4 | // Move semantics assert(b == c); move(a, b); // not C++0x assert(a == c); |
注意,在使用移动语义的赋值之后,不再保证
移动语义的一个好处是它允许在某些情况下进行优化。考虑以下常规值类型:
1 | struct A { T* x; }; |
假设我们定义两个
1 | bool operator==(const A& lhs, const A& rhs) { return *lhs.x == *rhs.x; } |
最后假设我们定义一个对象
1 2 3 | 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; } |
现在假设我们要定义一个函数来交换两个
我们可以用复制语义的正常方式来实现它。
1 2 3 4 5 6 | void swap(A& a, A& b) { A t = a; a = b; b = t; } |
然而,这是不必要的效率低下。我们在做什么?
- 我们创建了一个
a 到t 的拷贝。 - 然后我们把
b 复制到a 中。 - 然后将
t 复制到b 中。 - 最后,摧毁
t 。
如果
在我们的例子中,很容易在
1 2 3 4 5 6 | // Not C++0x void move(A& lhs, A& rhs) { lhs.x = rhs.x; rhs.x = nullptr; } |
我们只需将
定义了这个新的移动操作后,我们可以定义一个优化的交换:
1 2 3 4 5 6 7 | void swap(A& a, A& b) { A t; move(t, a); move(a, b); move(b, t); } |
移动语义的另一个优点是它允许您在无法复制的对象周围移动。这方面的一个主要例子是
C++0x允许移动语义通过其值引用特征。具体来说,此类操作:
1 | a = b; |
当
1 | a = std::move(b); |
要定义移动优化,需要定义移动构造函数和移动分配运算符:
1 2 | T::T(T&&); T& operator=(T&&); |
由于这些操作具有移动语义,因此可以自由修改传入的参数(前提是保持对象处于可破坏状态)。
结论基本上就是这样。注意,rValk引用也用于允许在C++ 0x中完全转发(由于特定的类型系统在R值引用和其他类型之间的交互),但这实际上与移动语义无关,所以我在这里没有讨论过。
型
基本上,右值引用允许您检测对象是临时的,而不必保留其内部状态。这允许使用更高效的代码,其中C++ 03一直都需要复制,在C++ 0x中,您可以继续使用相同的资源。此外,右值引用可以实现完美的转发。
看看这个答案。
型
很高兴看到这样一个问题,我很高兴分享我的观点。我想你是在询问一个关于C++语言本身的错误修复,而不仅仅是另一个C++语言的特性。"臭虫"已经存在了几十年了。也就是说,复制构造函数。
如果你知道在物理学中有很多东西是不能被复制的,比如能量和质量,复制构造器看起来很奇怪。这只是个玩笑,但事实上,在编程的世界里,像独占文件描述符这样的对象是不可复制的。所以C++程序员和设计师发明了一些技巧来解决这个问题。有三个著名的:NRVO,
nrvo(命名为返回值优化)是一种技术,它允许函数按值返回对象,而不调用复制构造函数。但是nrvo的问题是,尽管实际上没有调用copy构造函数,但仍然需要一个
1 2 3 4 5 6 | template <typename _T> auto_ptr(auto_ptr<_T>& source) { _ptr = source._ptr; // where _ptr is the pointer to the contained object source._ptr = NULL; } |
这根本不是复制品,而是"移动"。您可以将这种行为视为移动语义的原型。
但
这是痛苦的,直到C++0x移动语义最终由编译器制造商发布和实现。
简单地说,您可以将移动语义想象为与
顺便说一下,在C++ 0x中,EDCOX1〔1〕被禁止,并且推荐一种新的模板类型EDCOX1×8。
我的故事现在就要结束了。如果你想更多地了解它,比如奇怪的语法和右值系统,请参考其他文章。
型
我读了大约一年的大量文本解释,直到看了Scott Meyer的精彩演讲:http://skillsmatter.com/podcast/home/move-semanticsPerfect-forwarding-and-r value-references,才了解R值参考的所有内容。
他以一种有趣和缓慢的方式解释过程中发生的每一件事。
我知道,是1小时30分,但实际上,这是我去年得到的最好的解释。
在读完文章(像其他答案一样)后,看了这段视频后,我的脑海中确实以一种一致的方式将它融合在了一起,几天后,我才能够向一些同事解释它,并解释如何使用std::unique_ptr(因为它是相关的-它只允许移动语义,而不允许复制),因为它需要理解std::move(),即uires理解移动语义。