• std::move 移动对象资源


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

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

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

    std::move 的函数原型.

    template<typename _Tp>
      constexpr typename std::remove_reference<_Tp>::type&&
      move(_Tp&& __t) noexcept
      { return static_cast<typename std::remove_reference<_Tp>::type&&>(__t); }
    

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

    /// remove_reference
    template<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; };
    

    例子
    以下用两个例子来说明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 << ""
    ";
    
        // 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 << ""
    ";
    
        std::cout << "The contents of the vector are "" << v[0]
                                             << "", "" << v[1] << ""
    ";
    
    }
    

    输出:

    After copy, str is "Hello"
    After move, str is ""
    The contents of the vector are "Hello", "Hello"
    

    例子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 << ""
    ";
    
        // 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 << ""
    ";
    
        std::cout << "The contents of the vector are "" << v[0]
                                             << "", "" << v[1] << ""
    ";
    
    }
    
    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. 注意,第一个函数每个对象多调用了拷贝构造函数,多创建了一次,而使用了move操作的只是移动了资源 
    2. 注意,vector即使 push_back 第二个对象时,会移动第一个对象,很奇怪,如果你把注释去掉的话,会发现资源 Moving 很多次,这是 vector 实现影响了, 这个是因为push_back引发vector长度增长导致其内存重新分配,原来vector中的对象都被移动到新分配的内存上,所以会多次调用move构造函数,如果事先reverse,就不会发生这种情况.比较清楚的看出来 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.

    **转自 https://blog.csdn.net/infoworld/article/details/50736633**
  • 相关阅读:
    List of XML and HTML character entity references
    Windows 如何查看端口占用情况?
    input placeholder文字垂直居中(Mobile & PC)
    使用CSS3改变选中元素背景色
    登录远程SQL服务器
    写了一个简洁的删除重复行的case
    自动驾驶中的计算机视觉
    ImageNet图像分类大赛
    在IDEA中使用MyBatis Generator逆向工程生成代码
    利用浏览器LocalStorage缓存图片,视频文件
  • 原文地址:https://www.cnblogs.com/ymd12103410/p/9600514.html
Copyright © 2020-2023  润新知