我刚听完Scott Meyers关于C++0x的软件工程广播播客采访。大多数新特性对我来说都是有意义的,我现在对C++0x非常兴奋,只有一个例外。我仍然不懂移动语义。。。到底是什么?


当前回答

这就像复制语义,但不必复制所有数据,而是从“移动”的对象中窃取数据。

其他回答

假设您有一个返回实体对象的函数:

Matrix multiply(const Matrix &a, const Matrix &b);

当您编写这样的代码时:

Matrix r = multiply(a, b);

然后普通的C++编译器将为multiply()的结果创建一个临时对象,调用复制构造函数初始化r,然后销毁临时返回值。C++0x中的移动语义允许调用“移动构造函数”通过复制其内容来初始化r,然后丢弃临时值,而不必销毁它。

如果被复制的对象在堆上分配了额外的内存来存储其内部表示,这一点尤其重要(可能与上面的Matrix示例类似)。复制构造函数必须要么完整复制内部表示,要么在内部使用引用计数和写时复制语义。移动构造函数将不占用堆内存,只复制Matrix对象内的指针。

这就像复制语义,但不必复制所有数据,而是从“移动”的对象中窃取数据。

移动语义是指在没有人需要源值时转移资源,而不是复制资源。

在C++03中,对象经常被复制,只有在任何代码再次使用该值之前才会被销毁或赋值。例如,当您从函数中按值返回时,除非RVO踢入您返回的值,否则该值将被复制到调用方的堆栈帧,然后超出范围并被销毁。这只是众多示例中的一个:当源对象是临时对象时,请参阅passbyvalue;当超过其capacity()时,可以使用排序等算法重新排列项目。

当这种复制/销毁对很昂贵时,通常是因为对象拥有一些重量级资源。例如,vector<string>可能拥有一个动态分配的内存块,其中包含一个字符串对象数组,每个字符串对象都有自己的动态内存。复制这样的对象代价很高:必须为源中每个动态分配的块分配新的内存,并复制所有值。然后需要释放刚才复制的所有内存。然而,移动一个大向量<string>意味着只需将几个指针(指动态内存块)复制到目标,并在源中将它们清零。

你知道复制语义是什么意思吗?这意味着您有可复制的类型,对于用户定义的类型,您可以明确地编写复制构造函数和赋值运算符,或者编译器隐式地生成它们。这将复制一份。

移动语义基本上是一种用户定义的类型,带有构造函数,它接受一个非常量的r值引用(使用&&(是两个&符号)的新类型引用),这称为移动构造函数,赋值运算符也是如此。那么,移动构造函数做什么呢?它不是从源参数复制内存,而是将内存从源“移动”到目标。

你什么时候想这样做?那么std::vector就是一个例子,假设您创建了一个临时std::vector,然后从一个函数返回它,比如:

std::vector<foo> get_foos();

当函数返回时,复制构造函数将产生开销,如果(在C++0x中也是如此)std::vector有一个移动构造函数,而不是复制它。它只需要设置指针并将动态分配的内存“移动”到新实例。这有点像std::auto_ptr的所有权转移语义。

这是Bjarne Stroustrup的《C++编程语言》一书中的答案。如果你不想看视频,可以看下面的文字:

考虑一下这个片段。从运算符+返回涉及将结果从局部变量res复制到调用者可以访问的地方。

Vector operator+(const Vector& a, const Vector& b)
{
    if (a.size()!=b.size())
        throw Vector_siz e_mismatch{};
    Vector res(a.size());
        for (int i=0; i!=a.size(); ++i)
            res[i]=a[i]+b[i];
    return res;
}

我们真的不想要副本;我们只是想从函数中得到结果。所以我们需要移动Vector而不是复制它。我们可以如下定义移动构造函数:

class Vector {
    // ...
    Vector(const Vector& a); // copy constructor
    Vector& operator=(const Vector& a); // copy assignment
    Vector(Vector&& a); // move constructor
    Vector& operator=(Vector&& a); // move assignment
};

Vector::Vector(Vector&& a)
    :elem{a.elem}, // "grab the elements" from a
    sz{a.sz}
{
    a.elem = nullptr; // now a has no elements
    a.sz = 0;
}

&&表示“右值引用”,是我们可以绑定右值的引用。“rvalue”'旨在补充“lvalue”,它大致意思是“可以出现在赋值左侧的值”。因此,rvalue大致意思是:“不能赋值的值”,例如函数调用返回的整数,以及Vectors运算符+()中的res局部变量。

现在,语句返回res;不会复制!