C++11中右值引用與移動構造函數

編程語言 文章 C語言 技術 輕鬆學C語言 輕鬆學C語言 2017-08-28

C++11中引入了很多令人激動的新特性,右值引用與移動構造函數便是其中之一,作為一個剛剛接觸C++11標準的初學者,要理解這些東西還真不是一件簡單的事,我就在這上面花了不少時間,還好總算是對其有了一定的認識,於是就將自己的理解記錄下來,一來加深自己的理解,二來希望能夠對後面需要接觸這塊內容的朋友有所幫助。文章如有遺漏或理解不恰當之處,還希望指出。

左值和右值

在切入主題右值引用和移動構造函數之前,我們還是先理解兩個概念:左值和右值。對此,早期C語言給出的定義是:左值是一個表達式,可以出現在=的左邊或右邊;但右值只能出現在右邊。 那麼問題來了,哪些表達式可以出現在=的左邊呢?我回答不上,估計給出這個定義的大神也不好回答,這個定義實在太模糊,不好理解。

到了C++中可以這麼理解:對於一個表達式,凡是對其取地址(&)操作可以成功的都是左值,否則就是右值。好了,下面我們看幾個例子來加深下理解:

C++11中右值引用與移動構造函數

C/C++交流群,歡迎大家一起來交流提升。565122788

此外需要注意的是:臨時對象是右值

左值引用和右值引用

在理解了左值和右值之後,再來看與之相關的兩個概念,左值引用和右值引用。其實我們早就接觸過左值引用這個概念了,只不過在C++11之前,不存在右值引用,因此將左值引用直接稱為“引用”。也就是說,我們曾經一直用的int& x = y事實上是對於int類型左值的引用。

右值引用 是 C++ 11中引入的新特性 ,可以理解為對右值的引用,我們知道在C++中右值不能被修改。但右值引用的出現,打破了這個限制,它允許我們獲取右值的引用,並修改之,關於右值引用這個特點,在後面我們實現移動構造函數的時候會用到,這裡先知道這個概念即可。

右值引用的形式為:類型 && a= 被引用的對象 ,此外需要注意的是右值引用只能綁定到右值,如int && x = 3;而形如 int x = 3; int && y = x則是錯誤的,因為x是一個左值。

引用作為函數參數

我們早就知道,C++中的(左值)引用可以用作函數的參數,並且也建議儘可能用引用作為函數的參數,主要原因是傳引用比傳值效率更高。其實不光左值引用可以作為函數參數,在C++中,右值引用也能作為函數參數,下面看兩個函數。

C++11中右值引用與移動構造函數

對於以下語句的調用及輸出如下:

C++11中右值引用與移動構造函數

這是因為,左值引用只能綁定左值(const左值引用除外),因此foo(i)調用的是函數(1),右值引用只能綁定右值,因此foo(22)調用的是函數(2)。

拷貝構造函數的侷限性

我們來考慮一個例子,我們定義了一個工廠函數獲得Test對象,然後在main()函數中創建了一個Test對象 t ,然後將調用工廠函數獲得Test對象初始化 t ,運行程序,看看發生了什麼。

C++11中右值引用與移動構造函數

C++11中右值引用與移動構造函數

C++11中右值引用與移動構造函數

從運行結果來看,這個示例中調用了兩次拷貝構造函數構造了兩個臨時對象,一次是在instance函數返回時一次在對t的初始化。其實這兩個臨時對象並沒有什麼意義,構造完了馬上就析構了。但是就是因為這麼兩個無用的東西,在拷貝構造函數中執行了不必要的內存拷貝,這裡還好,只是拷貝了100個int類型的對象,如果是拷貝100000個甚至更多了,可想而知效率是多麼的低了。

移動構造函數

C++是個追求效率的語言,因此絕不容許那麼低效的設計出現,於是C++標準委員會那幫大鬍子就在想,有沒有可能將 在工廠函數當中所構造對象的成員變量(m_p)所指向的那塊內存“偷”過來,而不是重新開闢一塊內存,然後再將之前的內容複製過來呢? 這就是移動構造函數設計的思想

所謂移動構造函數,大家從名字上應該可以猜到:它應該就是一種構造函數,只不過它接受的參數是一個本類對象的右值引用,對於本例,移動構造函數的定義如下:

C++11中右值引用與移動構造函數

可以看到,在移動構造函數的初始化列表中,只做了一個淺拷貝m_p(rhs.m_p),將rhs對象已經申請的內存據為己用,同時將rhs的指針賦值為nullptr。這就避免了拷貝構造函數內存複製導致的效率問題。拷貝構造函數和移動構造函數在實現時其內存的變化如下圖所示。

C++11中右值引用與移動構造函數

C++課堂學習群:565122788,晚上8:00直.播.講.課,歡迎免.費.學.習

移動構造函數在用來構造臨時變量或者用臨時變量來構造對象的時候被調用,比如說,如果上面的例子在類中定義了移動構造函數,那麼例中調用拷貝構造函數的那兩處地方則應該調用移動構造函數。代碼如下。

C++11中右值引用與移動構造函數

C++11中右值引用與移動構造函數

C++11中右值引用與移動構造函數


從程序運行結果來看,我們的Test對象除了在工廠函數當中被使用默認構造函數構造一次之後,其餘調用的全部都是移動構造函數,避免了內存的無謂拷貝。

std::move

現在我們來看另外一種場景,在下面的情況下,我們知道在Test t2(t1)處會調用拷貝構造函數(t1是左值,因此不會調用移動構造函數),那麼有沒有一種辦法在此處也調用移動構造函數而不是拷貝構造函數呢?

C++11中右值引用與移動構造函數

答案是肯定的,C++11標準中給我們提供了std::move來解決這個問題,如下,只需將Test t2(t1)換成下面的語句即可:

C++11中右值引用與移動構造函數

C/C++交流群,歡迎大家一起來交流提升。565122788

這個std::move的作用就是將左值轉換為右值,以便調用移動構造函數。這裡有一點特別需要注意的是,在Test t2(std::move(t1))語句後,不能再對t1進行操作了,因為在移動構造函數中,已經將t1的成員變量m_p置為nullptr了。

為您提供通俗易懂的技術文章,讓技術變的更簡單!喜歡這篇文章的話記得關注哦!正在學習的小夥伴也可以加群一起來學習或探討C/C++。最後,謝謝大家的支持!!!

相關推薦

推薦中...