std::move

来源:互联网 发布:投资什么赚钱知乎 编辑:程序博客网 时间:2024/06/06 14:22

原文链接:http://blog.csdn.net/infoworld/article/details/50736633

场景:

  1. C++ 标准库使用比如vector::push_back 等这类函数时,会对参数的对象进行复制,连数据也会复制.这就会造成对象内存的额外创建, 本来原意是想把参数push_back进去就行了.
  2. C++11 提供了std::move 函数来把左值转换为xrvalue, 而且新版的push_back也支持&&参数的重载版本,这时候就可以高效率的使用内存了.
  3. 对指针类型的标准库对象并不需要这么做.

参考:

1. Move Constructors and Move Assignment Operators (C++) 
2. std::move

说明:

  1. std::move(t) 用来表明对象t 是可以moved from的,它允许高效的从t资源转换到lvalue上.
  2. 注意,标准库对象支持moved from的左值在moved 之后它的对象原值是有效的(可以正常析构),但是是unspecified的,可以理解为空数据,但是这个对象的其他方法返回值不一定是0,比如size().所以,moved from 之后的对象最好还是不要使用吧?(如有不正确理解,请告知)
  3. 对本身进行move,并赋值给本身是undefined的行为.
std::vector<int> v = {2, 3, 3};v = std::move(v); // undefined behavior
  • 1
  • 2
  • 1
  • 2

std::move 的函数原型.

/** *  @brief  Convert a value to an rvalue. *  @param  __t  A thing of arbitrary type. *  @return The parameter cast to an rvalue-reference to allow moving it.*/template<typename _Tp>  constexpr typename std::remove_reference<_Tp>::type&&  move(_Tp&& __t) noexcept  { return static_cast<typename std::remove_reference<_Tp>::type&&>(__t); }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

结构体 remove_reference 的原型,就是重载了多个结构体模板来获取原类型 type.

/// remove_referencetemplate<typename _Tp>  struct remove_reference  { typedef _Tp   type; };template<typename _Tp>  struct remove_reference<_Tp&>  { typedef _Tp   type; };template<typename _Tp>  struct remove_reference<_Tp&&>  { typedef _Tp   type; };
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

例子

以下用两个例子来说明std::move的用法.

例子1

– 原lvalue值被moved from之后值被转移,所以为空字符串. 
– 摘录自cppreference

void TestSTLObject(){    std::string str = "Hello";    std::vector<std::string> v;    // uses the push_back(const T&) overload, which means    // we'll incur the cost of copying str    v.push_back(str);    std::cout << "After copy, str is \"" << str << "\"\n";    // uses the rvalue reference push_back(T&&) overload,    // which means no strings will be copied; instead, the contents    // of str will be moved into the vector.  This is less    // expensive, but also means str might now be empty.    v.push_back(std::move(str));    std::cout << "After move, str is \"" << str << "\"\n";    std::cout << "The contents of the vector are \"" << v[0]                                         << "\", \"" << v[1] << "\"\n";}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

输出:

After copy, str is "Hello"After move, str is ""The contents of the vector are "Hello", "Hello"
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4

例子2

– 自定义自己的类对象支持moved from 操作,需要实现 Move Constructors and Move Assignment Operators

#include <iostream>#include <stdio.h>#include <utility>#include <vector>#include <string>class MemoryBlock{public:   // Simple constructor that initializes the resource.   explicit MemoryBlock(size_t length)      : _length(length)      , _data(new int[length])   {      std::cout << "In MemoryBlock(size_t). length = "                << _length << "." << std::endl;   }   // Destructor.   ~MemoryBlock()   {      std::cout << "In ~MemoryBlock(). length = "                << _length << ".";      if (_data != nullptr)      {         std::cout << " Deleting resource.";         // Delete the resource.         delete[] _data;      }      std::cout << std::endl;   }   // Copy constructor.   MemoryBlock(const MemoryBlock& other)      : _length(other._length)      , _data(new int[other._length])   {      std::cout << "In MemoryBlock(const MemoryBlock&). length = "                << other._length << ". Copying resource." << std::endl;      std::copy(other._data, other._data + _length, _data);   }   // Copy assignment operator.   MemoryBlock& operator=(const MemoryBlock& other)   {      std::cout << "In operator=(const MemoryBlock&). length = "                << other._length << ". Copying resource." << std::endl;      if (this != &other)      {         // Free the existing resource.         delete[] _data;         _length = other._length;         _data = new int[_length];         std::copy(other._data, other._data + _length, _data);      }      return *this;   }   // Retrieves the length of the data resource.   size_t Length() const   {      return _length;   }   // Move constructor.    MemoryBlock(MemoryBlock&& other)       : _data(nullptr)       , _length(0)    {       std::cout << "In MemoryBlock(MemoryBlock&&). length = "             << other._length << ". Moving resource." << std::endl;       // Copy the data pointer and its length from the       // source object.       _data = other._data;       _length = other._length;       // Release the data pointer from the source object so that       // the destructor does not free the memory multiple times.       other._data = nullptr;       other._length = 0;    }    // Move assignment operator.    MemoryBlock& operator=(MemoryBlock&& other)    {       std::cout << "In operator=(MemoryBlock&&). length = "                 << other._length << "." << std::endl;       if (this != &other)       {          // Free the existing resource.          delete[] _data;          // Copy the data pointer and its length from the          // source object.          _data = other._data;          _length = other._length;          // Release the data pointer from the source object so that          // the destructor does not free the memory multiple times.          other._data = nullptr;          other._length = 0;       }       return *this;    }private:   size_t _length; // The length of the resource.   int* _data; // The resource.};void TestSTLObject(){    std::string str = "Hello";    std::vector<std::string> v;    // uses the push_back(const T&) overload, which means    // we'll incur the cost of copying str    v.push_back(str);    std::cout << "After copy, str is \"" << str << "\"\n";    // uses the rvalue reference push_back(T&&) overload,    // which means no strings will be copied; instead, the contents    // of str will be moved into the vector.  This is less    // expensive, but also means str might now be empty.    v.push_back(std::move(str));    std::cout << "After move, str is \"" << str << "\"\n";    std::cout << "The contents of the vector are \"" << v[0]                                         << "\", \"" << v[1] << "\"\n";}void TestMyObjectWithoutUseMove(){   std::vector<MemoryBlock> v;   MemoryBlock mb1(25);   // MemoryBlock mb2(75);   // MemoryBlock mb3(50);   v.push_back(mb1);   //v.push_back(mb2);   //v.insert(v.begin() + 1, mb3);}void TestMyObjectWithUseMove(){   std::vector<MemoryBlock> v;   MemoryBlock mb1(25);   // MemoryBlock mb2(75);   // MemoryBlock mb3(50);   v.push_back(std::move(mb1));   //v.push_back(MemoryBlock(75));   //v.insert(v.begin() + 1, MemoryBlock(50));}int main(int argc, char const *argv[]){    //TestSTLObject();    TestMyObjectWithoutUseMove();    std::cout << "......................................." << std::endl;    TestMyObjectWithUseMove();    return 0;}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179

输出: 
1. 注意,第一个函数每个对象多调用了拷贝构造函数,多创建了一次,而使用了move操作的只是移动了资源 
2. 注意,vector即使 push_back 第二个对象时,会移动第一个对象,很奇怪,如果你把注释去掉的话,会发现资源 Moving 很多次,这是 vector 实现影响了,比较清楚的看出来 Move 的特性的就是 push_back 一个参数. 
3. 注意,g++ 4.8.1 的 vector push_back 多个对象时优化的没 vs 好,vs 是调用 Move 构造器,而 g++ 是调用 Copy 构造器,你会发现拷贝构造函数会调用很多次.

In MemoryBlock(size_t). length = 25.In MemoryBlock(const MemoryBlock&). length = 25. Copying resource.In ~MemoryBlock(). length = 25. Deleting resource.In ~MemoryBlock(). length = 25. Deleting resource........................................In MemoryBlock(size_t). length = 25.In MemoryBlock(MemoryBlock&&). length = 25. Moving resource.In ~MemoryBlock(). length = 0.In ~MemoryBlock(). length = 25. Deleting resource.
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
原创粉丝点击