Cpp / 右值、纯右值、将亡值
一、左值與右值
左值(lvalue)和右值(rvalue)是 C++ 類型系統之中的基礎概念,我們不需要了解這些基礎概念,同樣也能寫出代碼。但是如果沒有弄清左右值的概念,對于許多 C++ 高級特性的探索會一葉障目,所以筆者嘗試總結一下自己對于左值與右值的理解。
在 C++11 之前的版本,基本沿用了 C 語言之中對于左值與右值的定義,說起來也很簡單:“在C++之中的變量只有左值與右值兩種:其中凡是可以取地址的變量就是左值,而沒有名字的臨時變量,字面量就是右值”。 正是因為這兩種變量分別位于 = 的左右兩側,所以被命名為左值與右值。下面,舉個栗子:
int x; int y;x = 1; y = 2; x = y; y = x;// 以下代碼有誤 3 = x; x + y = 4;通過上述的代碼我們可以快速的理解,顯然 x,y 作為變量可以存在 = 的左側,而稱之為左值,而 3,x + y 作為字面量或中間結果,沒有辦法取得地址,則稱之為右值。 這里筆者也給一個簡單判定的左右值的方式:
判斷能否取值的地址,能取地址的就是左值。
二、將亡值
其實上一節對于左值右值的描述,在我們編寫絕大多數代碼的場景下并沒有什么影響。而在 C++11 擴展了右值的的概念,將右值分為了純右值(pure rvalue)與將亡值(eXpiring Value)。純右值的概念等同于我們之前所理解的右值,指的是臨時變量或字面量值;而將亡值是C++11新引入的概念,它依托于右值。
glvalue(泛左值) = lvalue (傳統意義上的左值)+ xvalue(消亡值,通過右值引用產生)
rvalue (傳統意義上的右值) = prvalue(純右值) + xvalue
?在C++之中,使用左值去初始化對象或為對象賦值時,會調用拷貝構造函數或賦值構造函數。而使用一個右值來初始化或賦值時,會調用移動構造函數或移動賦值運算符來移動資源,從而避免拷貝,提高效率。 而將亡值可以理解為通過移動構造其他變量內存空間的方式獲取到的值。在確保其他變量不再被使用、或即將被銷毀時,來延長變量值的生命期。而實際上該右值會馬上被銷毀,所以稱之為:將亡值。例如:
- 返回右值引用的函數的調用表達式。
- 轉換為右值引用的轉換函數的調用表達式。
上述概念闡述的略微抽象,我們來看下面這段代碼:
這是我們簡單定義的 Time 類,在類中我們定義了拷貝構造函數和移動構造函數:
#include <iostream>class Time { public:int *hour;int *minute;int *second;Time(int h, int m, int s){hour = new int(h);minute = new int(m);second = new int(s);}Time(const Time &t){std::cout << "copy contruct." << std::endl;hour = new int(*t.hour);minute = new int(*t.minute);second = new int(*t.second);}Time(Time &&t) noexcept : hour(t.hour), minute(t.minute), second(t.second){t.hour = nullptr;t.minute = nullptr;t.second = nullptr;std::cout << "move contruct." << std::endl;}~Time(){std::cout << "call ~Time()" << std::endl;delete hour;delete minute;delete second;} };接下來我們執行下面的代碼:
int main() {Time test(10,25,12);Time test2(test);return 0; }執行結果:
copy contruct. call ~Time() call ~Time()由上述代碼我們看到 test2 對象調用了拷貝構造函數來構造了新的對象,這個過程顯然是更占用內存的。而接下來,我們嘗試利用 move 函數將 test 強行轉化為將亡值,來避免內存重新分配的過程。但是之后我們也無法再訪問 test 對象的內容了,因為都在移動構造函數之中置為了空指針。
int main() {Time test(10,25,12);Time test2(std::move(test));return 0; }執行結果:
move contruct. call ~Time() call ~Time()通過這樣的方式來減少不必要的內存操作。但是之后我們也無法再訪問 test 對象的內容了,因為都在移動構造函數之中置為了空指針。將亡值通過移動構造函數“借尸還魂”,通過 test2 變量延續了自己的生命周期。
三、拓展
1、std::move 的作用是無論你傳給它的是左值還是右值,通過 std::move 之后都變成了右值。
2、++i 是左值,i++ 是右值。
前者,對 i + 1 后再賦給 i,最終的返回值就是 i,所以,++i 的結果是具名的,名字就是 i;而對于 i++ 而言,是先對 i 進行一次拷貝,將得到的副本作為返回結果,然后再對 i + 1,由于 i++ 的結果是對 i + 1前 i 的一份拷貝,所以它是不具名的。假設自增前i的值是 6,那么,++i 得到的結果是 7,這個 7 有個名字,就是 i ;而 i++ 得到的結果是 6,這個 6 是 i + 1 前的一個副本,它沒有名字,i 不是它的名字,i 的值此時也是 7。可見,++i 和 i++ 都達到了使 i + 1的目的,但兩個表達式的結果不同。
?
轉自:https://www.cnblogs.com/happenlee/p/9337776.html
(SAW:Game Over!)
總結
以上是生活随笔為你收集整理的Cpp / 右值、纯右值、将亡值的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: OS / Linux / 伙伴(budd
- 下一篇: Cpp / std::move 原理