程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> [C++11]_[初級]_[使用std::move移動對象資源]

[C++11]_[初級]_[使用std::move移動對象資源]

編輯:C++入門知識

[C++11]_[初級]_[使用std::move移動對象資源]


場景:

C++ 標准庫使用比如vector::push_back 等這類函數時,會對參數的對象進行復制,連數據也會復制.這就會造成對象內存的額外創建, 本來原意是想把參數push_back進去就行了. C++11 提供了std::move 函數來把左值轉換為xrvalue, 而且新版的push_back也支持&&參數的重載版本,這時候就可以高效率的使用內存了. 對指針類型的標准庫對象並不需要這麼做.

說明:

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 的函數原型.

/**
 *  @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
  constexpr typename std::remove_reference<_Tp>::type&&
  move(_Tp&& __t) noexcept
  { return static_cast::type&&>(__t); }

結構體 remove_reference 的原型,就是重載了多個結構體模板來獲取原類型 type.

/// remove_reference
template
  struct remove_reference
  { typedef _Tp   type; };

template
  struct remove_reference<_Tp&>
  { typedef _Tp   type; };

template
  struct remove_reference<_Tp&&>
  { typedef _Tp   type; };

例子

以下用兩個例子來說明std::move的用法.

例子1

– 原lvalue值被moved from之後值被轉移,所以為空字符串.
– 摘錄自cppreference

void TestSTLObject()
{
    std::string str = "Hello";
    std::vector 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";

}

輸出:

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 
#include 

#include 
#include 
#include 

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 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 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 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 實現影響了,比較清楚的看出來 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. 下一頁:
Copyright © 程式師世界 All Rights Reserved