移动语义 - 那是什么一回事呢? [重复]

问题描述 投票:10回答:4

可能重复: Can someone please explain move semantics to me?

可能有人点我一个很好的来源或解释在这里什么是移动语义?

c++ c++11 rvalue-reference move-semantics
4个回答
34
投票

忘记的时刻有关的C ++ 0x。移动语义的东西,是独立的语言 - 的C ++ 0x仅提供与移动语义执行操作的标准方式。

Definition

移动语义定义某些操作的行为。他们大多对比与复制语义的时间,所以这将是有益的第一个定义。

分配与拷贝语义有下列行为:

// 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保持与移动语义分配后保持不变任何保证。这是关键的区别。

Uses

移动语义的一个好处是,它允许在某些情况下的优化。请看下面的常规值类型:

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;
}

然而,这是不必要的低效率的。我们在做什么?

  • 我们创建a进入副本t
  • 然后,我们复制ba
  • 然后t复制到b
  • 最后,破坏t

如果T对象是复制再昂贵的,这是一种浪费。如果我问你换你的计算机上的两个文件,你就不会创建第三个文件,然后复制并摧毁你的临时文件之前,各地粘贴文件的内容,你会吗?不,你移动一个文件离开,移动第二到第一位置,最后移到第一个文件回第二。无需复制数据。

在我们的例子中,可以很容易地周围型A的对象移动:

// 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

的C ++ 0x允许通过其右值参考要素移动语义。具体而言,那种操作:

a = b;

有移动语义时b是右值引用(拼写T&&),否则他们有拷贝语义。您可以通过使用std::move功能时move不是右值引用(从我前面定义的b不同)迫使移动语义:

a = std::move(b);

std::move是一个简单的函数,基本上投射其参数右值参考。需要注意的是表达式的结果(如函数调用)是自动右值引用,这样你就可以利用在这些情况下移动语义不改变你的代码。

要定义移动的优化,你需要定义一个移动构造函数和移动赋值运算符:

T::T(T&&);
T& operator=(T&&);

由于这些操作具有移动语义,你可以自由地修改传递的参数(前提是你留下的破坏状态的对象)。

Conclusion

这是几乎所有有它。需要注意的是右值引用也被用来允许C ++ 0x中完美转发(由于右值引用和其他类型的特制型系统交互),但是,这不是真正涉及到移动的语义,所以我还没有讨论在这里。


4
投票

基本上,右值引用允许你当对象是临时工,你不必保留其内部状态检测。这使得其中C ++ 03以前要复制所有的时间,在C ++ 0x中,你可以继续重复使用相同的资源更高效的代码。此外,右值引用实现完美转发。

看一看this answer


2
投票

我读了一吨的文字解释了大约一年,并没有掌握有关R值引用的一切,直到我看到由Scott迈耶这个优秀的演示:http://skillsmatter.com/podcast/home/move-semanticsperfect-forwarding-and-rvalue-references

他在某种程度上这是有趣的解释和缓慢不够了解这种情况发生在过程中每一件事情。

我知道,这1小时30分钟,但实际上,它是我曾在去年的最好说明。

在已经阅读文章(像其他的答案),观看这部影片并一起在我的脑海里融化以一致的方式和几天后我能解释给一些同事,并解释如何使用std ::的unique_ptr(如这是有关 - 它仅允许移动语义,而不是复制),因为它需要的std ::移动(),这需要理解移动语义的理解。


2
投票

很高兴看到这样的问题,我很高兴与大家分享我的观点。我想你问有关C ++语言本身,而不是只是一个C ++语言特性的指定一个bug修复。 “虫”已经存在了数十年。也就是说,拷贝构造函数。

拷贝构造函数,如果你在物理学知道有很多是无法复制像能量和质量的东西,看起来很奇怪。这只是一个笑话,但事实上在世界上节目也一样,像独家文件描述符对象是不可拷贝。所以C ++程序员和设计师发明了一些技巧来面对这一切。有3名:NRVO,boost::noncopyablestd::auto_ptr

NRVO(命名返回值优化)是一个TECHNIC,让一个函数返回值的对象,而不调用拷贝构造函数。但随着NRVO的问题是,虽然拷贝构造函数实际上并没有叫,一个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;
}

这是不是在所有的副本,而是一个“移动”。您可以考虑这种行为的举动语义的原型。

std::auto_ptr也有自己的问题:它是不是与STL容器兼容。所以,不幸的是,约不可复制什么是痛苦的。

这是痛苦的,直到在的C ++ 0x移动语义终于出版,由编译器厂商实施。

在简单的方式,你可能只是想招语义的东西一样std::auto_ptr的“复制”行为,而是由语言功能的完整支持,因此它正常工作与容器和算法。

通过C ++ 0x中的方式std::auto_ptr已过时,建议新的模板类型std::unique_ptr

我的故事现在将结束。如果你想知道更多关于它像奇怪的语法和右值系统请参考其他职位。

© www.soinside.com 2019 - 2024. All rights reserved.