1. 程式人生 > >左值、右值與右值引用 & C++11中

左值、右值與右值引用 & C++11中

我們先來談談C++11中對左右值的判斷標準,以及左右值本身的一些細節,我想這應該是故事的開始。

   在C++11中可以取地址的、有名字的就是左值,反之,不能取地址的、沒有名字的就是右值。
   舉個例子:在a=b+c;中,&a是允許的操作,但&(b+c)這樣的操作則不會通過編譯。因此a是一個左值,(b+c)是一個右值。

   其次,在C++11中右值又分為將亡值(xvalue,eXpiring Value)和純右值(prvalue,Pure Rvalue)。
   其中純右值的概念等同於我們在C98標準中所謂的右值概念,講的是用於辨識臨時變數和一些不跟物件關聯的值
   比如class a{...};a fun(){...}函式返回的臨時例項,比如a=1+3;中1+3產生的臨時變數值,再比如a=true;中的true,上述這些都是純右值。


   而將亡值是C++11中新提出的一個概念。
這裡讀者最關心的應該是兩個問題:1.將亡值概念的提出是為了解決什麼問題?2.將亡值的定義到底是什麼?

   我們來從問題的起源一步步講起,希望在最後解答這兩個問題。
   大家都知道,C++中一般函式或方法的實參以及返回值都是以副本的形式傳遞的(除非你特別指定了按引用來傳遞,而這也僅限於實參)。
   來看一段程式碼
#include <iostream>

using namespace std;

class A

{

	public:

		//建構函式(初始化p_int,並附上i的值)

		A(int i)

			p_int=new int(i);

			cout<<"call A(int i)"<<endl;
		}

		//==============================

		//拷貝建構函式(對p_int實現了深拷貝)

		A(const A &a)
		{ 

			p_int=new int(*(a.p_int));

			cout<<"call A(const A &a)"<<endl; 

		}

		//==============================

		//解構函式(釋放p_int)

		~A()

			cout<<"call ~A()"<<endl; 

			delete p_int; 

		}

		//==============================

		int *p_int;

};

//傳入一個A物件,並直接返回該物件

A fun(A a){return a;}

int main()

{

	A test(10);

	fun(test);

	return 1;

}
   執行結果    程式碼分析
首先我們關閉了g++編譯器的自動優化功能,來看到c++最本質的執行狀態,關閉自動優化的引數是-fno-elide-constructors。
  
 因為A test(10);程式碼,是我們呼叫了A的普通建構函式,獲得了螢幕輸出call A(int i)。
   因為將test傳入fun()中使用了按副本傳遞,所以呼叫了A的拷貝建構函式,獲得了螢幕輸出call A(const A &a)。
   因為fun()中的return a;也是按副本傳遞的,所以再一次的呼叫了A的拷貝建構函式,獲得了螢幕輸出call A(const A &a)。
   至此程式執行的螢幕輸出應該是清晰且沒有任何疑問的。
同時由於類的成員涉及指標型別,所以我們的拷貝建構函式使用了深拷貝。
   在這段程式碼中,我只顯性的建立了一次A的物件,但是卻產生了巨大的開銷。
   為p_int開闢記憶體就達3次之多,如果是實際身材環境中,真實的類可能包含更多的成員涉及更多的記憶體。


   我們仔細思考一下,兩次拷貝建構函式的呼叫都是由於程式產生了一個副本導致的。
而這些副本物件其實並沒有任何實際意義,而且轉瞬即逝,在完成了自己的傳遞任務後就即可銷燬了。這恰恰和將亡值想要表達的意思十分吻合,不是嗎?
   第一個問題似乎已經有了答案,但將亡值的概念能為我們解決實際問題呢?

如果已經可以明確,我的拷貝源是一個將亡值,那麼我們其實並沒有深拷貝的必要,而是隻要將他的資源移位即用就可以。
   所以C++11提供了一個所謂的移動建構函式,接受一個將亡值作為拷貝源。

   來看一段程式碼
#include <iostream>

#include <iostream>

using namespace std;

class A

{

	public:

		//建構函式(初始化p_int,並附上i的值)

		A(int i)
			p_int=new int(i);
			cout<<"call A(int i)"<<endl;

		}

		//==============================

		//拷貝建構函式(對p_int實現了深拷貝)

		A(const A &a)

		{ 

			p_int=new int(*(a.p_int));

			cout<<"call A(const A &a)"<<endl; 

		}

		//==============================

		//移動建構函式(把將亡值的指標據為己用)

		A(A &&a):p_int(a.p_int)

		{

			a.p_int=nullptr;

			cout<<"call A(A &&a)"<<endl; 


            
           

相關推薦

引用 & C++11

我們先來談談C++11中對左右值的判斷標準,以及左右值本身的一些細節,我想這應該是故事的開始。    在C++11中可以取地址的、有名字的就是左值,反之,不能取地址的、沒有名字的就是右值。    舉個例子:在a=b+c;中,&a是允許的操作,但&(b+c)這樣的操

執行緒互斥鎖(C++11std::thread和std::mutex的用法)

執行緒 0 首先是曾經在MultiCMOS專案中用到的: #include <thread> //包含標頭檔案 class IDataProcessUnit { protected:

c++11 引用 move forward

再次來寫左值右值相關的東西我的內心是十分惴惴不安的,一來這些相關的概念十分不好理解,二來網上相關的文章實在太多了,多少人一看這類題目便大搖其頭,三來也怕說不清反而誤導了別人,反覆糾纏這些似乎無關大雅的語言細節實在也有成為 language lawyer 之嫌。但我還是決定再總結一次,因為這是我一直以來學習新

C++11的`移動語義``引用`的介紹討論

本文主要介紹了C++11中的移動語義與右值引用, 並且對其中的一些坑做了深入的討論. 在正式介紹這部分內容之前, 我們先介紹一下rule of three/five原則, 與copy-and-swap idiom最佳實踐. 本文參考了stackoverflow上的一些回答. 不能算是完全原創 rule

Effective C++筆記之一:宣告定義初始化

一.宣告(Declaration)        區分宣告和定義可以讓C++支援分開編譯,宣告常常見於標頭檔案中。原始檔包含標頭檔案之後,就可以使用這個變數,即使沒有看到該變數的定義。 宣告的語法如下: extern int i; // object decl

Python淺拷貝深拷貝的區別

賦值,其實就是物件的引用,對新物件的如何修改都會影響到原始物件。 Python中有兩種拷貝操作:淺拷貝和深拷貝。 copy.copy(x):返回x的淺拷貝。 copy.deepcopy(x):返回x的深拷貝。 那麼都是拷貝,淺拷貝和深拷貝有什麼不同嗎? 淺

C 判斷 —— if...else 語句(bool變數float變數指標變數“零”進行比較)(else 到底哪個 if 配對呢? if 語句後面的分號?)

1、bool 變數與“零值”進行比較 bool 變數與“零值”進行比較的 if 語句怎麼寫? bool bTestFlag = FALSE;//想想為什麼一般初始化為 FALSE 比較好? A), if(bTestFlag == 0); if(bTestFlag == 1

python 分詞自定義詞表停用詞詞頻統計(tfidf)詞性標註部分詞性刪除

# -*- coding: utf-8 -*- """ Created on Tue Apr 17 15:11:44 2018 @author: NAU """ ##############分詞、自定義詞表、停用詞################ import jieba 

C++拷貝構造移動構造返回優化

拷貝建構函式 拷貝建構函式(又稱複製建構函式),是用來建立已存在物件的副本。對應的還有一個概念是拷貝賦值運算子,當需要顯示地宣告拷貝建構函式時,一般建議同時宣告拷貝賦值運算子,以使得程式碼的含義明確。 如果不宣告拷貝建構函式(或拷貝賦值運算子),編譯器將會生一個預設的

C++11的universal引用引用

stackoverflow上有個問題:Why “universal references” have the same syntax as rvalue references? 就是說為什麼這倆的形式都是T&&(T表示一個型別)。其中有一個回答很

2.1詳解變數的定義初始化

在前的課程中我們談到,變數其實就是一塊記憶體空間的名稱。簡要地說,計算機擁有可供程式使用的隨機存取儲存器(RAM),當一個變數被定義時,一部分記憶體就會被預留給這個變數。 記憶體的最小單位是二進位制數字(binary digit,bit,位元),0或者1。你可以把bi

c++ 11emplace_back替代push_back的相關知識點,含引用,move用法等

C++11引入了右值引用,轉移建構函式,push_back()右值時就會呼叫建構函式和轉移建構函式(原來是呼叫拷貝構造,會為臨時變數申請堆空間,影響程式效率,C++11以後為右值引用呼叫轉移建構函式,不會為臨時變數申請堆空間,而是直接賦值,提高程式效率)。 使用mplace_back替代push_back()

【Python】關於鍵盤鍵入str的或非問題?【報錯:TypeError: unsupported operand type(s) for |: 'str' and 'str'】

error 運算符 字符 符號 str == 條件 col one 參考 【報錯:TypeError: unsupported operand type(s) for |: ‘str‘ and ‘str‘】   在進行鍵入值比較的時候,想要用“或&rd

js物件的直接賦淺拷貝深拷貝

  最近Vue專案中寫到一個業務,就是需要把對話方塊的表單中的資料,每次點選提交之後,就存進一個el-table表格中,待多次需要的表單資料都提交進表格之後,再將這個表格提交,實現多個表單資料的同時提交,期間還可以用表格進行預覽、修改等其他操作。將每個表單資料存進表格的程式碼大致程式碼如下:     let&

ForkJoin有參無返回有參有返回實例

.com turn sys int dao tps end 並且 ota 介紹:   a . Fork/Join為JKD1.7引入,適用於對大量數據進行拆分成多個小任務進行計算的框架,最後把所有小任務的結果匯總合並得到最終的結果   b . 相關類 public abst

vue學習十一(prop傳不同 v-bind 動態賦單向資料流prop校驗)

區域性註冊 在這些情況下,你可以通過一個普通的 JavaScript 物件來定義元件: var ComponentA = { /* ... */ } 然後在 components 選項中定義你想要使用的元件: new Vue({ el: '#app',

MySQL開發技巧 第二禪(子查詢匹配兩個解決同屬性多過濾的問題計算累進稅的問題)

一、如何在子查詢中匹配兩個值     mysql子查詢的使用場景及其好處         1、什麼是子查詢?           

Vue.js實現雙向資料繫結(表單自動賦表單自動取

1、使用Vue.js實現雙向表單資料繫結,例子 <!--html程式碼--> <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta

【Android基礎】頁面跳轉(Activity跳轉)

一個Android應用程式很少會只有一個Activity物件,如何在多個Activity之間進行跳轉,而且能夠互相傳值是一個很基本的要求。 本次我們就講一下,Android中頁面跳轉以及傳值的幾種方式! Activity跳轉與傳值,主要是通過Intent類來連線多個A

Vue(2)- v-model局部組件和全局組件父子組件傳平行組件傳

star component handle lec 開發 div 復制 line 定義 一、表單輸入綁定(v-model 指令)   可以用 v-model 指令在表單 <input>、<textarea> 及 <select> 元素上