C++11/14標準(一)左值與右值

切記所有代碼需要在C++11標準下運行,如果使用g++,請添加-std=c++11。

  • 左值和右值

    左值是一個用來存儲數據的變量,有自己的內存空間,表達式結束後仍然存在。右值是一個臨時變量,它在表達式結束時生命週期終止。

通俗的說法就是能使用取地址符的值就是左值,不能使用取地址符的值就是右值。

    左值可以是函數或者對象。

///
 /// @file    lvalue.cpp
 /// @author  kogan([email protected])
 /// @date    2020-06-16 09:11:18
 ///
 
#include <iostream>
using std::cout;
using std::endl;

class Empty
{
//面試中常會問的問題,空類佔一個字節。因爲空類實例化之後就會擁有獨一無二的內存地址
};

void TestFunc(void);

int main()
{
  int variable = 5;
  //int型變量取地址。取地址後,&variable是右值。
  &variable;

  //&&variable; //編譯會報錯,所以註釋。

  //類也是左值
  Empty empty;
  &empty;

  //函數也是左值
  &TestFunc;  
  return 0;
}

void TestFunc(void)
{
}
  • 右值引用

    使用T&&的形式表示右值引用。T&爲左值引用。

///
 /// @file    llvalue.cpp
 /// @author  kogan([email protected])
 /// @date    2020-06-17 10:12:38
 ///
 
#include <iostream>
using std::cout;
using std::endl;

int main()
{
    
  int x = 0;
  //左值引用
  int &r1 = ++x;
  //右值引用
  int &&r2 = x++;
  //常亮左值引用
  const int &r3 = x++;
    //此時r2延長了x++返回的值的生命週期。
  cout << r2 << endl;
  return 0;
}

    這裏提及一下,前置++和後置++。先說結論前置++效率更高。前置++返回一個左值。後置++返回一個右值。

 


前置++本質是:
self& operator++()
{  
  ++current; 
  return(*this);
}

後置++本質是:
self operator++(int)
{  
  self tmp = *this;  
  ++(*this);  
  return tmp;
}

self是指對象類型。後置++需要創建一個臨時對象,然後將原來對象的值賦值給臨時對象。然後對象自增,返回臨時對象。同時爲了區分前置++和後置++,後置++中函數參數有一個0。這也就是後置++中帶有一個int參數。

    

    代碼如下:


///
 /// @file    prefix_and_suffixpp.cpp
 /// @author  kogan([email protected])
 /// @date    2020-06-17 09:24:10
 ///
 
#include <iostream>
using std::cout;
using std::endl;

class Int
{
public:
  Int():m_variable(0)
  {
  }

  int operator++(int i)
  {  
    cout << "suffix ++ "  << endl;
    int tmp = (this->m_variable);  
    ++(this->m_variable);  
    return tmp;
  }
    
  int& operator++()
  {  
    cout << "prefix ++ "  << endl;
    ++(this->m_variable); 
    return((this->m_variable));
  }
private:
  int m_variable;
};

int main()
{
  Int i;
  
  cout << "---------------i++-------------------" << endl;
  cout << "i = " << i++ << endl;
  cout << "---------------++i-------------------" << endl;
  cout << "i = " << ++i << endl;
  
  return 0;
}

運行結果爲:

  • 移動語義

    引用摺疊(reference collapsing):對左值引用或者右值引用再進行左值引用是T&,右值引用不變化。函數參數使用T&&將保持原型不變。

    此時需要介紹std::move()函數。

    move()聲明:

template <class T>
typename remove_reference<T>::type && move(T&& t) noexcept;
//等價於
static_cast<T &&> t;

 

該函數主要作用是將函數參數轉換成匿名的右值引用(T&&)。

    C++11/14中爲class增加了參數類型爲T&&的轉移構造函數和轉移賦值函數,只要類實現了這兩個特殊函數就能利用右值對象進行零成本構造。

    

 ///
 /// @file    moveable.cpp
 /// @author  kogan([email protected])
 /// @date    2020-06-17 10:56:47
 ///
 
#include <iostream>
#include <utility>
using std::cout;
using std::endl;

class moveable
{
public:
  moveable () = default;
  moveable (moveable&& other)
  {
    cout << "moveable destructor &&" << endl;
    std::swap(x, other.x);
  }

  moveable& operator = (moveable&& other)
  {
    std::swap(x, other.x);
    return *this;
  }
  
    static moveable create()
{
    moveable obj;
    return obj;
  }
private:
  int x;
};

int main()
{
  moveable m1;    //缺省構造函數
  moveable m2(std::move(m1));  //調用轉移構造函數,m1被轉移
  moveable m3 = moveable::create();  //調用轉移賦值函數

  return 0;
}

C++標準庫中string、vector、deque等組件都實現了轉移構造函數和轉移賦值函數。

emplace()、emplace_back()使用右值插入元素。

  • 完美轉發

    forward(),在泛型編程時轉發,可以把函數參數原封不動地轉發給其他函數。

    template<class T> T&& forward(T&& t) noexcept;


void check(int &&)
{
  cout << "rvalue" << endl;
}

template<typename T>
void print(T&& v)
{
  check(std::forward<T> (v);
 }

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章