C 11 右值引用和移動語義

2022-02-02 23:02:58 字數 3858 閱讀 1747

因為工作室要求寫技術部落格記錄學習到的知識點,自己之前是沒有寫過部落格的,所以現在用一篇介紹右值引用和移動語義的部落格作為部落格的第一篇,可能對於移動語義的理解還不夠深刻,但可以作為乙個簡單的介紹部落格

要理解好右值引用首先要知道什麼是左值?什麼是右值?

1.左值是表示式結束後依然存在的持久化物件

2.右值則是表示式結束時就不再存在的值

便捷區別方法:對表示式取位址,如果能,是左值,否則是右值

int a=10; 

int b=5; //這是乙個左值

&(a+b); //這是乙個右值

//區別方法

&a //因為a是個左值,可以對其取位址

&(a+b) //因為(a+b)是個臨時變數,是右值,並不能對其取位址

由於修飾符不同可以分為非常量左值和常量左值

1.非常量左值只能繫結左值

2.常量左值是個奇葩,因為他不僅可以繫結左值(非常量和常量),也能夠繫結右值

為什麼它能夠繫結右值呢?

在於他們繫結了右值後,延長了右值的生命週期,使之像乙個左值一樣,但有乙個東西是只能讀不能改

int a=10;       

int c=10;//非常量左值

const int b=5; //常量左值

int& d=a; //可以

int& d=a+c; //報錯 a+b是右值

int& d=b; //報錯 b是常量左值

const int& e=a;

const int& e=b;

const int& e=a+c;

//上面的三條式子都不會報錯,因為常量左值都可以繫結

在c++11中新增了右值引用的概念,用&&來表示右值引用

原本右值是個臨時變數,表示式結束時其生命也就結束了,但可以通過右值引用使其被儲存到特定的位置,且可以獲得該位置的位址

換句說:左值引用可以比作乙個擁有姓名的人給其取個別名,而右值引用則是乙個沒有姓名的人然後給其取個別名

int a=10;

int b=10;

int&& c=a+b; //乙個簡單的右值引用

移動語義是實際檔案還留在原來的地方,而只修改記錄.

用下面乙個useless類來說明(具體實現就不說了)

#ifndef __useless_h

#define __useless_h

#includeclass useless

;#endif // __useless_h

#include "useless.h"

int useless::ct = 0;

useless::useless()

useless::useless(int k): n(k)

useless::useless(int k, char ch):n(k)

useless::useless(const useless & f):n(f.n)

useless::useless(useless && f):n(f.n)

useless::~useless()

useless useless::operator+(const useless & f) const

void useless::showdata() const

else

std::cout << std::endl;

}void useless::showobject() const

#include"useless.h"

#includeint main()

system("pause");

return 0;

}

首先看下複製建構函式

useless two = one;把one物件(左值)賦給了two,在這期間會呼叫複製建構函式useless(const useless& f)來實現複製;

再來看看useless four(one + three)這條語句,one+three會呼叫operatoe+()來產出了乙個右值,這個右值作為引數呼叫了useless(useless && f)這個建構函式,

我們稱其為移動建構函式(你沒有提供的話系統會提供乙個預設的移動建構函式)

現在再深入**下一些函式的具體實現:

一丶首先把移動建構函式的宣告和定義注釋掉

1.首先引數one+three裡面呼叫乙個建構函式建立乙個temp物件,然後通過乙個複製建構函式來建立乙個臨時複製物件,然後指向了這個物件(即函式返回了這個臨時物件);接下來,刪除這個temp物件;

useless useless::operator+(const useless & f) const

2.因為常量左值引用可以繫結右值,所以會呼叫下面這個函式

useless::useless(const useless & f):n(f.n)

所以會新建乙個four物件.使其使用這個臨時物件中的記憶體,接下來刪除了這個臨時物件

這個表明了總共建立了三個物件

這裡可以呼叫了複製建構函式,但其只呼叫了一次

因為編譯器會優化返回值,然後在編譯時加上-fno-elide-constructors選項即可關閉返回值優化

在gcc編譯時關閉這個返回值可以看到其呼叫了兩次複製建構函式

二丶把移動建構函式的宣告和定義的注釋取消掉

1.這個one+three同樣呼叫了operator+()這個函式

2.(1)因為引數one+three是個臨時物件,是個右值,所以編譯器會先呼叫移動建構函式而不是複製建構函式

(2)與複製建構函式不同的是,他直接指向了這個臨時物件,然後把原來指向這個臨時物件的指標改為nullptr(相當於乙個物件的所有權的轉移,把臨時物件的所以權奪了過來)

(3)把原來指向臨時物件的指標改為nullptr是讓這個物件析構不會使原來的記憶體被釋放掉,不然這個移動建構函式沒有意義

這樣便省去了複製建構函式中建立乙個臨時物件的過程,總共建立了兩個物件;

可以看到呼叫了一次移動建構函式,而一些無關的工作量減少

而移動語義目的之一就是消除這些額外的工作.

一旦工作量變大,就會導致一些額外的資源申請和釋放的操作;使用移動語義就既能夠夠節省資源,也能夠節省時間

當然除了移動建構函式,還有移動賦值函式(在這裡就不講了,大致原理是一樣的

std::move()在標頭檔案utility

有時候左值是乙個區域性變數,即表明他也是有臨時的生命週期,那麼能不能也是呼叫移動語義而不是複製語義呢?

c++11提供了std::move()來解決這個問題,他可以把左值強制轉化為右值引用,使左值可用於移動語義中

useless four(one);  //呼叫複製建構函式

useless four(std::move(one)); //呼叫移動建構函式

如果我們沒有提供移動建構函式,std::move()會失效但不會報錯,因為會呼叫複製建構函式(const &)

可能有待續寫......

出處:

移動語義和右值引用(C 11)

左值 可以取位址 有名字的 右值 不能取位址 沒名字的 傳統的c 引用稱為左值引用,使得標誌符關聯到左值。c 11新增了右值引用,使用 表示。引入右值引用的主要目的之一是實現移動語義。在複製物件時,實現物件的移動而非拷貝。通過移動建構函式 移動賦值運算子實現 函式的引數為右值引用,函式內部並非深度複...

C 11新特性 移動語義和右值引用

傳統的c 引用 左值引用 使得識別符號關聯到左值。左值是乙個表示資料的表示式 如變數名或解除引用的指標 程式可以獲得其位址。c 11新增了右值引用。右值引用,顧名思義,可以關聯到右值,即 可以出現在賦值表示式的右邊,但不能對其應用位址運算子的值。右值包括字面常量 c風格字串除外,它表示位址 諸如x ...

C 11 右值引用與move語義

1.右值引用 1.1 右值 右值就是指在下乙個分號後 更準確的說是在包含右值的完整表示式的最後 銷毀的臨時物件。對於c 11,編譯器會依據引數是左值還是右值在複製建構函式和move建構函式間進行選擇。怎樣區分呢?the distinguishing criterion is if it has a ...