c语言 变量的左值和右值,C++雾中风景10:聊聊左值,纯右值与将亡值
C++11的版本在類型系統(tǒng)上下了很大的功夫,添加了諸如auto,decltype,move等新的關(guān)鍵詞來(lái)簡(jiǎn)化代碼的編寫(xiě)與降低閱讀代碼的難度。為了更好的理解這些新的語(yǔ)義,筆者確定通過(guò)幾篇文章來(lái)簡(jiǎn)單窺探一下C++類型系統(tǒng)的冰山一角,如果加深了對(duì)C++類型系統(tǒng)的理解,想必大家也能夠更好的應(yīng)用由C++11帶給我們的新"利器"。
1.左值與右值
左值(lvalue)和右值(rvalue)是C++類型系統(tǒng)之中的基礎(chǔ)概念,我們不需要了解這些基礎(chǔ)概念,同樣也能寫(xiě)出代碼。但是如果沒(méi)有弄清左右值的概念,對(duì)于許多C++高級(jí)特性的探索會(huì)一葉障目,所以筆者嘗試總結(jié)一下自己對(duì)于左值與右值的理解。
在C++11之前的版本,基本沿用了C語(yǔ)言之中對(duì)于左值與右值的定義,說(shuō)起來(lái)也很簡(jiǎn)單:“在C++之中的變量只有左值與右值兩種:其中凡是可以取地址的變量就是左值,而沒(méi)有名字的臨時(shí)變量,字面量就是右值”。 正是因?yàn)檫@兩種變量分別位于=的左右兩側(cè),所以被命名為左值與右值。下面,舉個(gè)栗子:
int x;
int y;
x = 1;
y = 2;
x = y;
y = x;
// 以下代碼有誤
3 = x;
x + y = 4;
通過(guò)上述的代碼我們可以快速的理解,顯然x,y作為變量可以存在=的左側(cè),而稱之為左值,而3,x + y作為字面量或中間結(jié)果,沒(méi)有辦法取得地址,則稱之為右值。 這里筆者也給一個(gè)簡(jiǎn)單判定的左右值的方式:
判斷能否取值的地址,能取地址的就是左值。
2.將亡值
其實(shí)上一節(jié)對(duì)于左值右值的描述,在我們編寫(xiě)絕大多數(shù)代碼的場(chǎng)景下并沒(méi)有什么影響。而在C++11擴(kuò)展了右值的的概念,將右值分為了純右值(pure rvalue)與將亡值(eXpiring Value)。純右值的概念等同于我們之前所理解的右值,指的是臨時(shí)變量或字面量值;而將亡值是C++11新引入的概念,它依托于右值。
在C++之中,使用左值去初始化對(duì)象或?yàn)閷?duì)象賦值時(shí),會(huì)調(diào)用拷貝構(gòu)造函數(shù)或賦值構(gòu)造函數(shù)。而使用一個(gè)右值來(lái)初始化或賦值時(shí),會(huì)調(diào)用移動(dòng)構(gòu)造函數(shù)或移動(dòng)賦值運(yùn)算符來(lái)移動(dòng)資源,從而避免拷貝,提高效率。 而將亡值可以理解為通過(guò)移動(dòng)構(gòu)造其他變量?jī)?nèi)存空間的方式獲取到的值。在確保其他變量不再被使用、或即將被銷(xiāo)毀時(shí),來(lái)延長(zhǎng)變量值的生命期。而實(shí)際上該右值會(huì)馬上被銷(xiāo)毀,所以稱之為:將亡值。
上述概念闡述的略微抽象,我們來(lái)看下面這段代碼:
這是我們簡(jiǎn)單定義的Time類,在類中我們定義了拷貝構(gòu)造函數(shù)和移動(dòng)構(gòu)造函數(shù):
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) {
cout << "copy" << 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;
cout << "move" << endl;
}
~Time() {
cout << "call ~Time()" << endl;
delete hour;
delete minute;
delete second;
}
};
接下來(lái)我們執(zhí)行下面的代碼:
int main()
{
Time test(10,25,12);
Time test2(test);
return 0;
}
執(zhí)行結(jié)果:
copy
call ~Time()
call ~Time()
由上述代碼我們看到test2對(duì)象調(diào)用了拷貝構(gòu)造函數(shù)來(lái)構(gòu)造了新的對(duì)象,這個(gè)過(guò)程顯然是更占用內(nèi)存的。而接下來(lái),我們嘗試?yán)胢ove函數(shù)將test強(qiáng)行轉(zhuǎn)化為將亡值,來(lái)避免內(nèi)存重新分配的過(guò)程。但是之后我們也無(wú)法再訪問(wèn)test對(duì)象的內(nèi)容了,因?yàn)槎荚谝苿?dòng)構(gòu)造函數(shù)之中置為了空指針。
int main()
{
Time test(10,25,12);
Time test2(move(test));
return 0;
}
執(zhí)行結(jié)果:
move
call ~Time()
call ~Time()
通過(guò)這樣的方式來(lái)減少不必要的內(nèi)存操作。但是之后我們也無(wú)法再訪問(wèn)test對(duì)象的內(nèi)容了,因?yàn)槎荚谝苿?dòng)構(gòu)造函數(shù)之中置為了空指針。將亡值通過(guò)移動(dòng)構(gòu)造函數(shù)”借尸還魂“,通過(guò)test2變量延續(xù)了自己的生命周期。
3.左值的一些"坑"
雖然筆者給出了左右值分辨的一些基本標(biāo)準(zhǔn),但是還是有下面一些很讓人迷惑的場(chǎng)景:
條件表達(dá)式返回左值true ? i : i;
++i++ // 左值
++i // 右值
[]數(shù)組取值返回左值i[10]
指針取值操作符返回左值*i
字符串字面量返回左值“hello world”
這是一些表示左值的特殊情況,這里筆者也不展開(kāi)一一贅述了,希望大家可以簡(jiǎn)單的進(jìn)行記憶。當(dāng)然,筆者從來(lái)不去記一些太瑣碎的問(wèn)題,因?yàn)樘麟y記了,所以在C++11之中,可以標(biāo)準(zhǔn)庫(kù)中添加的模板類is_lvalue_reference來(lái)判斷表達(dá)式是否為左值,is_rvalue_reference來(lái)判斷是否為右值。
cout << is_lvalue_reference::value << endl;
cout << is_rvalue_reference::value << endl;
返回1則為真,0為假。
4.小結(jié)
這只是我們對(duì)C++類型系統(tǒng)的第一篇探討,后續(xù)筆者還會(huì)繼續(xù)深入的探討有關(guān)C++11新特性之中與類型系統(tǒng)相關(guān)的內(nèi)容,歡迎大家多多討論,指教。
內(nèi)容來(lái)源于網(wǎng)絡(luò)如有侵權(quán)請(qǐng)私信刪除
總結(jié)
以上是生活随笔為你收集整理的c语言 变量的左值和右值,C++雾中风景10:聊聊左值,纯右值与将亡值的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: k近邻算法原理c语言,实验二 K-近邻算
- 下一篇: 【C++】 C++标准模板库(一) Ve