C++ 什么是移动语义?

C++ 什么是移动语义?,c++,c++-faq,c++11,move-semantics,C++,C++ Faq,C++11,Move Semantics,我刚听完软件工程广播。大多数新特性对我来说都是有意义的,我现在对C++0x感到兴奋,除了一个。我还是不明白。。。具体是什么?假设您有一个返回实体对象的函数: Matrix multiply(const Matrix &a, const Matrix &b); 当您编写这样的代码时: Matrix r = multiply(a, b); unique_ptr<Shape>&& flawed_attempt() // DO NOT DO THIS!

我刚听完软件工程广播。大多数新特性对我来说都是有意义的,我现在对C++0x感到兴奋,除了一个。我还是不明白。。。具体是什么?

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

Matrix multiply(const Matrix &a, const Matrix &b);
当您编写这样的代码时:

Matrix r = multiply(a, b);
unique_ptr<Shape>&& flawed_attempt()   // DO NOT DO THIS!
{
    unique_ptr<Shape> very_bad_idea(new Square);
    return std::move(very_bad_idea);   // WRONG!
}
class Foo
{
    unique_ptr<Shape> member;

public:

    Foo(unique_ptr<Shape>&& parameter)
    : member(parameter)   // error
    {}
};

然后,普通C++编译器将为乘法()/CODE的结果创建临时对象,调用复制构造函数初始化 R>代码>,然后破坏临时返回值。C++0x中的移动语义允许调用“移动构造函数”,通过复制其内容来初始化

r
,然后丢弃临时值而不必对其进行破坏


如果要复制的对象在堆上分配额外内存以存储其内部表示,这一点尤其重要(可能与上面的
矩阵
示例类似)。复制构造函数必须创建内部表示的完整副本,或者内部使用引用计数和写时复制语义。移动构造函数将不使用堆内存,只在
矩阵
对象中复制指针。

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

Matrix multiply(const Matrix &a, const Matrix &b);
当您编写这样的代码时:

Matrix r = multiply(a, b);
unique_ptr<Shape>&& flawed_attempt()   // DO NOT DO THIS!
{
    unique_ptr<Shape> very_bad_idea(new Square);
    return std::move(very_bad_idea);   // WRONG!
}
class Foo
{
    unique_ptr<Shape> member;

public:

    Foo(unique_ptr<Shape>&& parameter)
    : member(parameter)   // error
    {}
};

然后,普通C++编译器将为乘法()/CODE的结果创建临时对象,调用复制构造函数初始化 R>代码>,然后破坏临时返回值。C++0x中的移动语义允许调用“移动构造函数”,通过复制其内容来初始化

r
,然后丢弃临时值而不必对其进行破坏


如果要复制的对象在堆上分配额外内存以存储其内部表示,这一点尤其重要(可能与上面的
矩阵
示例类似)。复制构造函数必须创建内部表示的完整副本,或者内部使用引用计数和写时复制语义。移动构造函数将不使用堆内存,只在
矩阵
对象中复制指针。

这类似于复制语义,但不必复制所有数据来从“移动”的对象中窃取数据。

这类似于复制语义,但是你不必复制所有的数据就可以从被“移动”的对象中窃取数据。

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

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

你想什么时候做?例如,std::vector创建了一个临时std::vector,并从函数返回它,比如:

std::vector<foo> get_foos();
std::vector get_foos();

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

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

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

你想什么时候做?例如,std::vector创建了一个临时std::vector,并从函数返回它,比如:

std::vector<foo> get_foos();
std::vector get_foos();

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

移动语义基于右值引用
右值是一个临时对象,它将在表达式末尾被销毁。在当前C++中,rValk只绑定到<代码> const 引用。C++1x将允许非
常量
右值引用,拼写为
T&
,它们是对右值对象的引用。
由于右值将在表达式末尾消失,因此可以窃取其数据。将其数据移动到另一个对象中,而不是将其复制到另一个对象中

class X {
public: 
  X(X&& rhs) // ctor taking an rvalue reference, so-called move-ctor
    : data_()
  {
     // since 'x' is an rvalue object, we can steal its data
     this->swap(std::move(rhs));
     // this will leave rhs with the empty data
  }
  void swap(X&& rhs);
  // ... 
};

// ...

X f();

X x = f(); // f() returns result as rvalue, so this calls move-ctor
在上面的代码中,使用
x
的复制构造函数,将
f()
的结果复制到
x
中。如果编译器支持移动语义,并且
X
具有移动构造函数,则会调用该构造函数。因为它的
rhs
参数是一个右值,我们知道它不再需要了,我们可以窃取它的值。
因此,该值被
f()
返回的未命名临时值移动到
x
(而初始化为空
x
x的数据被移动到临时值中,
auto_ptr<Shape> a(new Triangle);

      +---------------+
      | triangle data |
      +---------------+
        ^
        |
        |
        |
  +-----|---+
  |   +-|-+ |
a | p | | | |
  |   +---+ |
  +---------+

auto_ptr<Shape> b(a);

      +---------------+
      | triangle data |
      +---------------+
        ^
        |
        +----------------------+
                               |
  +---------+            +-----|---+
  |   +---+ |            |   +-|-+ |
a | p |   | |          b | p | | | |
  |   +---+ |            |   +---+ |
  +---------+            +---------+
auto_ptr(auto_ptr& source)   // note the missing const
{
    p = source.p;
    source.p = 0;   // now the source no longer owns the object
}
auto_ptr<Shape> a(new Triangle);   // create triangle
auto_ptr<Shape> b(a);              // move a into b
double area = a->area();           // undefined behavior
auto_ptr<Shape> make_triangle()
{
    return auto_ptr<Shape>(new Triangle);
}

auto_ptr<Shape> c(make_triangle());      // move temporary into c
double area = make_triangle()->area();   // perfectly safe
auto_ptr<Shape> variable(expression);
double area = expression->area();
auto_ptr<Shape> c(make_triangle());
                                  ^ the moved-from temporary dies right here
            lvalue   const lvalue   rvalue   const rvalue
---------------------------------------------------------              
X&          yes
const X&    yes      yes            yes      yes
X&&                                 yes
const X&&                           yes      yes
void some_function(std::string&& r);

some_function("hello world");
template<typename T>
class unique_ptr
{
    T* ptr;

public:

    T* operator->() const
    {
        return ptr;
    }

    T& operator*() const
    {
        return *ptr;
    }
    explicit unique_ptr(T* p = nullptr)
    {
        ptr = p;
    }

    ~unique_ptr()
    {
        delete ptr;
    }
    unique_ptr(unique_ptr&& source)   // note the rvalue reference
    {
        ptr = source.ptr;
        source.ptr = nullptr;
    }
unique_ptr<Shape> a(new Triangle);
unique_ptr<Shape> b(a);                 // error
unique_ptr<Shape> c(make_triangle());   // okay
    unique_ptr& operator=(unique_ptr&& source)   // note the rvalue reference
    {
        if (this != &source)    // beware of self-assignment
        {
            delete ptr;         // release the old resource

            ptr = source.ptr;   // acquire the new resource
            source.ptr = nullptr;
        }
        return *this;
    }
};
    unique_ptr& operator=(unique_ptr source)   // note the missing reference
    {
        std::swap(ptr, source.ptr);
        return *this;
    }
};
unique_ptr<Shape> a(new Triangle);
unique_ptr<Shape> b(a);              // still an error
unique_ptr<Shape> c(std::move(a));   // okay
        expressions
          /     \
         /       \
        /         \
    glvalues   rvalues
      /  \       /  \
     /    \     /    \
    /      \   /      \
lvalues   xvalues   prvalues
unique_ptr<Shape> make_triangle()
{
    return unique_ptr<Shape>(new Triangle);
}          \-----------------------------/
                  |
                  | temporary is moved into c
                  |
                  v
unique_ptr<Shape> c(make_triangle());
unique_ptr<Shape> make_square()
{
    unique_ptr<Shape> result(new Square);
    return result;   // note the missing std::move
}
unique_ptr<Shape>&& flawed_attempt()   // DO NOT DO THIS!
{
    unique_ptr<Shape> very_bad_idea(new Square);
    return std::move(very_bad_idea);   // WRONG!
}
class Foo
{
    unique_ptr<Shape> member;

public:

    Foo(unique_ptr<Shape>&& parameter)
    : member(parameter)   // error
    {}
};
class Foo
{
    unique_ptr<Shape> member;

public:

    Foo(unique_ptr<Shape>&& parameter)
    : member(std::move(parameter))   // note the std::move
    {}
};
X::X(const X&);              // copy constructor
X& X::operator=(const X&);   // copy assignment operator
X::~X();                     // destructor
X::X(X&&);                   // move constructor
X& X::operator=(X&&);        // move assignment operator
X& X::operator=(X source)    // unified assignment operator
{
    swap(source);            // see my first answer for an explanation
    return *this;
}
template<typename T>
void foo(T&&);
foo(make_triangle());   // T is unique_ptr<Shape>, T&& is unique_ptr<Shape>&&
unique_ptr<Shape> a(new Triangle);
foo(a);                 // T is unique_ptr<Shape>&, T&& is unique_ptr<Shape>&
#include <type_traits>

template<typename T>
typename std::enable_if<std::is_rvalue_reference<T&&>::value, void>::type
foo(T&&);
template<typename T>
typename std::remove_reference<T>::type&&
move(T&& t)
{
    return static_cast<typename std::remove_reference<T>::type&&>(t);
}
class A
{
   int i, *p;

public:
   A(const A& a) : i(a.i), p(new int(*a.p)) {}
   ~A() { delete p; }
};
class A
{
   int i, *p;

public:
   // Movement of an object inside a copy constructor.
   A(const A& a) : i(a.i), p(a.p)
   {
     a.p = nullptr; // pointer invalidated.
   }

   ~A() { delete p; }
   // Deleting NULL, 0 or nullptr (address 0x0) is safe. 
};
void heavyFunction(HeavyType());
class A
{
   int i, *p;

public:
   // Copy
   A(const A& a) : i(a.i), p(new int(*a.p)) {}

   // Movement (&& means "rvalue reference to")
   A(A&& a) : i(a.i), p(a.p)
   {
      a.p = nullptr;
   }

   ~A() { delete p; }
};
class Heavy
{
   bool b_moved;
   // staff

public:
   A(const A& a) { /* definition */ }
   A(A&& a) : // initialization list
   {
      a.b_moved = true;
   }

   ~A() { if (!b_moved) /* destruct object */ }
};
void some_function(A&& a)
{
   other_function(a);
}
other_function(std::move(a));
template<typename T>
void some_function(T&& a)
{
   other_function(std::forward<T>(a));
}
 `A& && == A&`
 `A&& && == A&&`
T f(T o) { return o; }
  //^^^ new object constructed
T b = f(a);
  //^ new object constructed
// Copy constructor
T::T(T &old) {
    copy_data(m_a, old.m_a);
    copy_data(m_b, old.m_b);
    copy_data(m_c, old.m_c);
}
// Move constructor
T::T(T &&old) noexcept {
    m_a = std::move(old.m_a);
    m_b = std::move(old.m_b);
    m_c = std::move(old.m_c);
}
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;
}
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;
}