切記所有代碼需要在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;
∅
//函數也是左值
&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);
}