一、引用程序員
C++中的引用主要用做函數的形參,接近於const指針,必須在建立時初始化。數組
以Person類爲例,以下:函數
Person p; //調用P的構造函數,建立對象P指針
Person &p2 = p; //引用變量P2指向P對象
Person p3 = p2; //P2是引用,建立一個p3的對象,會調用Person的拷貝構造函數,p3和p不是一個對象。字符串
Person &P4 = get(p); //形參是引用,因此參數傳入時不會再生成一個臨時對象。get
//返回一個引用,並將P4指向該引用。原型
Person P5 = get(p); //形參是引用,因此參數傳入時不會再生成一個臨時對象。編譯器
//返回一個引用,因爲要建立一個P5的對象,因此會調用拷貝構造函數生成一個對象。編譯
Person& get(Person &p)
{
return p;
}
實際上,如今的C++標準對於形參爲const引用的C++函數,若是實參不匹配,那麼編譯器會生成臨時變量,其行爲也相似按值傳遞,爲確保原始數據不被修改,將使用臨時變量來存儲值。因此儘量地使用const。
上述提到的實參與引用參數不匹配的狀況主要有兩種:
一、實參類型正確,可是不是左值。
二、實參類型不正確,可是能夠轉換爲正確的類型。
那什麼是左值呢?左值參數便是能夠被引用的數據對象,程序能夠獲取其地址,例如,變量、數組、對象、解引用的指針等都是左值。非左值包括字面常量(用引號括起來的字符串不算)和多項式,以及函數返回的臨時對象(引用除外)。const變量也是左值,是一種特殊的左值,屬於不可修改的左值。
以上的引用咱們又稱之爲左值引用,C++11中新增了另外一種引用,即右值引用。這種引用能夠指向右值,使用&&聲明。
double &&r3 = 5 + 9;//r3關聯到13
將右值關聯到右值引用將致使該右值被存儲到特定的位置,且能夠獲取該位置地址。即咱們雖然不能將取地址符用於14,可是咱們能夠用在r3上。這樣咱們就可使用右值引用來訪問該數據。
引入右值引用的主要目的之一是實現移動語義。
二、構造函數、拷貝構造函數、賦值運算符
在講移動語義以前,先說一下構造函數、拷貝構造函數、賦值構造函數。C++爲咱們提供了4個特殊的成員函數,除了上面3個之外還包括析構函數,這裏就很少說了。
Person();
Person(const Person & p);
Person& operator=(const Person &p);
函數原型如上所示,下面講一下拷貝構造函數和賦值運算符的區別,以及什麼時候會調用拷貝構造函數,什麼時候會調用賦值運算符。
二者最根本區別在於:拷貝構造函數是用來建立對象,賦值運算符是用來將一個對象的值複製給另一個對象。調用的是拷貝構造函數仍是賦值運算符,主要是看是否有新的對象實例產生。若是產生了新的對象實例,那調用的就是拷貝構造函數;若是沒有,那就是對已有的對象賦值,調用的是賦值運算符。
如下一樣也Person爲例
Person p = Person(); //構造函數
Person p1 = p; //p1對象不存在,p對象存在,調用拷貝構造函數
p1 = p; //p1和p對象都存在,調用賦值運算符
Person p2(p); //p2對象不存在,p對象存在,調用拷貝構造函數
Test::get(); //函數內部p對象建立,調用構造函數
//函數返回時臨時對象不存在,p對象存在,調用拷貝構造函數
//釋放內部p對象;接着釋放臨時對象
Person p3 = Test::get();//函數內部p對象建立,調用構造函數
//函數返回時臨時對象不存在,p對象存在,調用拷貝構造函數
//釋放內部p對象;
Test::get(p3); //什麼都不調用
Person P4 = Test::get(p3);//調用一次拷貝構造函數,生成對象P4
class Test
{
public:
static Person& get(Person &p)
{
return p;
}
static Person get()
{
Person p;
return p;
}
};
三、移動語義、移動構造函數、移動賦值運算符
首先咱們分析下爲何須要移動語義。
先看一下C++11以前的複製過程
Person p(get());
static Person get()
{
Person p2;
return p2;
}
首先會建立函數內對象p2,而後調用拷貝構造函數建立一個臨時對象,接着再調用拷貝構造函數建立對象p,而後再將臨時對象刪除。這時就作了冗餘的工做,若是直接將p與臨時對象關聯起來,那豈不就少作了不少工做嗎?這相似於計算機中移動文件的情形:實際文件還留在原來的地方,而只是修改了記錄,這種方法咱們稱爲移動語義。移動語義實質上沒有移動數據,而只是修改了記錄。
怎麼樣才能使用移動語義呢?
必須讓編譯器知道何時須要使用何時不須要使用。
首先咱們定義一個移動構造函數,這時就可使用右值引用了,它使用右值引用做爲參數,該引用關聯到右值實參,負責調整記錄,將全部權轉移給新對象的過程當中,移動構造函數可能會修改其實參,這意味着右值引用參數不該該是const。移動構造函數以下所示
Person(Person &&p)
使用的時候,須要傳入右值。以下所示
Person P(P1+P2);
移動語義只在消除額外的工做,機智的編譯器可能自動消除額外的賦值工做,但經過使用右值引用,程序員能夠指出什麼時候使用移動語義。
除了構造函數外,賦值運算符也可使用移動語義。原型以下
Person& operator=( Person && p);
移動構造函數和移動賦值運算符都是用右值,若是想要使用左值,該如何辦呢?可使用std::move函數,該函數將左值轉換成右值。對於大多數程序員來講,右值引用的好處並不是讓他們可以編寫使用右值引用的代碼,而是可以利用右值引用實現移動語義的庫代碼。例如,STL類如今都有拷貝構造函數,移動構造函數,複製賦值運算符,移動賦值運算符。
一般狀況下編譯器將提供6個特殊的成員函數,可是,若是您提供了析構函數、拷貝構造函數和複製賦值運算符,那麼編譯器將不提供移動構造函數和移動賦值運算符,相反,若是提供了移動構造函數和移動賦值運算符,那麼編譯器將不會提供拷貝構造函數和複製運算符。