溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

C++特性之引用   (Boolan)

發(fā)布時間:2020-09-28 10:12:47 來源:網(wǎng)絡(luò) 閱讀:462 作者:Haley_2013 欄目:編程語言

C++特性之引用   (Boolan)

本章內(nèi)容:

1 引用的不同用例

1.1 引用變量

1.2 引用數(shù)據(jù)成員

1.3 引用參數(shù)

1.4 引用作為返回值

1.5 使用引用還是指針

1.6 右值引用


1 引用

    在C++中,引用是變量的別名。所有對引用的修改都會改變被引用的變量的值??蓪⒁卯?dāng)作隱式指針,這個指針沒有取變量地址和解除引用的麻煩。

也可以將引用當(dāng)作原始變量的另一種名稱??梢詣?chuàng)建單獨(dú)的引用變量,在類中使用引用數(shù)據(jù)成員,將引用作為函數(shù)和方法的參數(shù),也可以讓函數(shù)或者方法返回引用。


1.1 引用變量

    引用變量在創(chuàng)建時必須初始化,如下:

int ival = 3;
int &iRef = x;

賦值后,iRef就是ival的另一個名稱。使用iRef就是使用ival的當(dāng)前值。對iRef賦值會改變ival的值。

無法在類外面聲明一個引用而不初始化它:

int &emptyRef;  //編譯出錯

不能創(chuàng)建對未命名值(例如一個整數(shù)字面值)的引用,除非這個引用是一個const值。在下面的示例中,unnamedRef1將無法編譯,因?yàn)檫@是一個針對常量的non-const引用。

這條語句意味著可以改變常量5的值,而這樣做沒有意義。由于unnamedRef2是一個const引用,因此可以運(yùn)行,不能寫成"unnamedRef2=7"。

int &unnamedRef1 = 5;        //編譯出錯
const int &unnamedRef2 = 5;  //正常運(yùn)行

(1) 修改引用

    引用總是引用初始化的那個變量:引用一旦創(chuàng)建,就無法修改。這一規(guī)則導(dǎo)致了許多令人迷惑的語法。如果在聲明一個引用時用一個變量"賦值",那么這個引用就指向這個變量。

然而,如果在此后使用變量對引用賦值,被引用變量的值就變?yōu)橘x值變量的值。引用不會更新為指向這個變量。示例代碼如下:

int x = 3,y = 4;
int &iRef = x;
iRef = y;//Changes value of x to 4. Doesn't make iRef refer to y.

如果試圖在賦值時取y的地址,以繞過這一限制:

int x = 3,y = 4;
int &iRef = x;
iRef = &y;        //編譯出錯

上面的代碼無法編譯。y的地址是一個指針,但iRef聲明為一個int的引用,而不是一個指針的引用。

    如果將一個引用賦值給另一個引用時,只是修改了其指向的值,而不是修改所指向的引用變量。(在初始化引用之后無法改變引用所指的變量;而只能改變該變量的值。)

(2) 指針的引用和指向引用的指針

    可以創(chuàng)建任何類型的引用,包括指針類型。下面給出一個指向int指針的引用例子:

int *pVal;
int *&ptrRef = pVal;
ptrRef = new int;
*ptrRef = 5;

    這一語法有一點(diǎn)奇怪:你可能不習(xí)慣看到*和&彼此相鄰。然而,該語義上很簡單:ptrRef是pVal的引用,pVal是一個指向int的指針。修改ptrRef會更改pVal。指針的引用很少見,但是在某些場合下很有用,在1.3節(jié)中會討論這一內(nèi)容。

    注意:

    (i)對引用取地址的結(jié)果與被引用變量取地址的結(jié)果是相同的。

    (ii)無法聲明引用的引用,或者指向引用的指針。


1.2 引用數(shù)據(jù)成員

    類的數(shù)據(jù)成員可以引用,但是如果不是指向其他變量,引用就無法存在。因此,必須在構(gòu)造函數(shù)初始化器(constructor initializer)中初始化引用數(shù)據(jù)成員,而不是在構(gòu)造函數(shù)體

內(nèi)。下面舉例說明:

class MyClass
{
public:
    MyClass(int &iRef):m_ref(iRef) {}
private:
  int &m_ref;
};


1.3 引用參數(shù)

    C++程序員通常不會單獨(dú)使用引用變量或者引用數(shù)據(jù)成員。引用經(jīng)常用作函數(shù)或者方法的參數(shù)。默認(rèn)的參數(shù)傳遞機(jī)制是值傳遞:函數(shù)接收參數(shù)的副本。修改這些副本時,原始的參數(shù)

保持不變。引用允許指定另一種向函數(shù)傳遞參數(shù)的語義:按引用傳遞。當(dāng)使用引用參數(shù)時,函數(shù)將引用作為參數(shù)。如果引用被修改,最初的參數(shù)變量也會修改。下面給出交換兩個數(shù)的例子來說明:

void swap(int &first, int &second)
{
    int temp = first;
    first = second;
    second = temp;
}

可以采用下面的方式調(diào)用這個函數(shù):

int x = 5, y = 6;
swap(x, y);

    當(dāng)使用x和y做參數(shù)調(diào)用函數(shù)swap()時,first參數(shù)初始化為x的引用,second參數(shù)初始化為y的引用。當(dāng)swap()修改first和second時,x和y實(shí)際上也被修改了。

就像無法使用常量初始化普通引用變量一樣,不能將常量作為參數(shù)傳遞給按引用傳遞參數(shù)的函數(shù):

swap(3, 4);    //編譯出錯

(1) 指針轉(zhuǎn)換為引用

    某個函數(shù)或者方法需要一個引用做參數(shù),而你擁有一個指向被傳遞值的指針,這是一種常見的困境。在此情況下,可以對指針解除引用(dereferencing),將指針"轉(zhuǎn)換"為引用。

這一行為會給出指針?biāo)傅闹担S后編譯器用這個值初始化引用參數(shù)。例如,可以這樣調(diào)用swap():

int x = 5, y = 6;
int *px = &x;
int *py = &y;
swap(*px, *py);

(2) 按引用傳遞與按值傳遞

    如果要修改參數(shù),并修改傳遞給函數(shù)或者方法的變量,就需要使用按引用傳遞。然而,按引用傳遞的用途并不局限于此。按引用傳遞不需要將參數(shù)副本復(fù)制到函數(shù),在有些情況下

會帶來兩面的好處:

    (i)效率:復(fù)制較大的對象或者結(jié)構(gòu)需要較長的時間。按引用傳遞只是把指向?qū)ο蠡蛘呓Y(jié)構(gòu)的指針傳遞給函數(shù)。

    (ii)正確性:并非所有對象都允許按值傳遞,即使允許按值傳遞的對象,也可能不支持正確的深度復(fù)制(deep copying)。(如果需要深度復(fù)制,動態(tài)分配內(nèi)存的對象必須提供自定

義復(fù)制構(gòu)造函數(shù)。)

    如果要利用好這些好處,但不想修改原始對象,可將參數(shù)標(biāo)記為const,從而實(shí)現(xiàn)按常理引用傳遞參數(shù)。按引用傳遞的這些優(yōu)點(diǎn)意味著,只有在參數(shù)是簡單的內(nèi)建類型(int或double

),且不需要修改參數(shù)的情況下才應(yīng)該使用按值傳遞。其他情況下都應(yīng)該按引用傳遞。


1.4 引用作為返回值

    可以讓函數(shù)或者方法返回一個引用,這樣做的主要作用是提高效率。返回對象的引用不是返回整個對象可以避免不必要的復(fù)制。當(dāng)然,只有涉及的對象在函數(shù)終止之后仍然存在的

情況才能使用這一技巧。(如果變量的作用域局限于函數(shù)或者方法,例如:堆棧中分配的變量,在函數(shù)結(jié)束時會被銷毀。這個時候絕對不能返回這個變量的引用。)

    返回引用的另一個原因是希望將返回值直接賦為左值(lvalue)(賦值語句在左邊)。一些重載的運(yùn)算符通常會返回引用。


1.5 使用引用還是指針

    在C++中,引用有可能被認(rèn)為是多余的:幾乎所有使用引用可以完成的任務(wù)都可以用指針來代替完成。例如,可以這樣編寫swap()函數(shù):

void swap(int *first, int *second)
{
    int temp = *first;
    *first = *second;
    *second = temp;
}

    然而,這些代碼不如使用引用版本那么清晰:引用可以使程序整潔并易于理解。此外,引用比指針安全:不可能存在無效的引用,也不需要顯式地解除引用,因此不會遇到像指針

那樣的解除引用問題。

    大多數(shù)情況下,應(yīng)該使用引用而不是指針。對象的引用甚至可以像指向?qū)ο蟮闹羔樐菢又С侄鄳B(tài)性。只有在需要改變所指地址時,才需要使用指針,因?yàn)闊o法改變引用所致的對像

。例如,動態(tài)分配內(nèi)存時,應(yīng)該將結(jié)果存儲在指針而不是引用中。需要使用指針的第二種情況是可選參數(shù)。例如,指針參數(shù)可以定義為帶默認(rèn)值nullptr的可選參數(shù),而引用參數(shù)不能這樣定義。

    還有一種方法可以判斷使用指針還是引用作為參數(shù)和返回類型:考慮誰擁有內(nèi)存。如果接受變量的代碼負(fù)責(zé)釋放相關(guān)對象的內(nèi)存,必須使用指向?qū)ο蟮闹羔?,最好是智能指針,這

是傳遞擁有權(quán)的推薦方式。如果接受變量的代碼不需要釋放內(nèi)存,那么應(yīng)該使用引用。

    注意:如果不需要改變所指的地址,就應(yīng)該使用引用而不是指針。

1.6 右值引用

    在C++中,左值(lvalue)是可以獲取其地址的一個量,例如一個有名稱的變量。由于經(jīng)常出現(xiàn)在賦值語句的左邊,因此稱其為左值。另一方面,所有不是左值的量都是右值(rvalue)

,例如常量值,臨時對象或者臨時值。通常右值位于賦值運(yùn)算符的右邊。

    右值引用是一個對右值(rvalue)的引用。特別地,這是一個當(dāng)右值是臨時對象時使用的概念。右值引用的目的是提供在涉及臨時對象時可以選用的特定方法。由于知道臨時對象會

被銷毀,通過右值引用,某些涉及復(fù)制大量值的操作可以通過簡單的復(fù)制指向這些值的指針來實(shí)現(xiàn)。

    函數(shù)可以將&&作為參數(shù)說明的一部分(例如 type&&name),來指定右值引用參數(shù)。通常,臨時對象被當(dāng)作const type&,但當(dāng)函數(shù)重載使用了右值引用時,可以解析臨時對象,

用于該重載。下面的示例說明了這一點(diǎn)。代碼首先定義了兩個incr()函數(shù),一個接受左值引用;另一個接受右值引用:

// Increment value using lvalue reference parameter.
void incr(int &value)
{
    cout << "increment with lvalue reference" << endl;
    ++value;
}
// Increment value using rvalue reference parameter.
void incr(int &&value)
{
    cout << "increment with rvalue reference" << endl;
    ++value;
}

    可以使具有名稱的變量作為參數(shù)調(diào)用incr()函數(shù)。于是a是一個具有名稱的變量,因此調(diào)用接受左值引用的incr()函數(shù)。調(diào)用完incr()后,a的值將是11。

int a = 10, b = 20;
incr(a);//調(diào)用incr(int &value);

    還可以用表達(dá)式作為參數(shù)來調(diào)用inrc()函數(shù)。此時無法使用接受左值引用作為參數(shù)的incr()函數(shù),因?yàn)楸磉_(dá)式a+b的結(jié)果是臨時的,這不是一個左值。在此情況下,會調(diào)用右值引用

版本。由于參數(shù)是一個臨時值,當(dāng)incr()函數(shù)調(diào)用結(jié)束后,會丟失這個增加的值。

incr(a + b); //將調(diào)用incr(int &&value);

    字面量也可以作inrc()調(diào)用的參數(shù),此時同樣會調(diào)用右值引用版本,因?yàn)樽置媪坎荒茏鳛樽笾怠?/span>

incr(3);    //將調(diào)用incr(int &&value);

    如果刪除接受左值引用的incr()函數(shù),使用名稱的變量調(diào)用incr(),例如:incr(b),此時會導(dǎo)致編譯錯誤,因?yàn)橛抑狄脜?shù)(int &&value)永遠(yuǎn)不會與左值(b)綁定。如下所示可

以使用std::move()將左值轉(zhuǎn)換為右值,強(qiáng)迫編譯器調(diào)用incr()的右值版本。當(dāng)incr()調(diào)用結(jié)束后,b的值為21。

incr(std::move(b));    //將調(diào)用incr(int &&value);

    右值引用并不局限于函數(shù)的參數(shù)??梢月暶饔抑狄妙愋偷淖兞?,并對其賦值,盡管這種用法并不常見。查下看如下代碼:

int &i = 2;//invalid:reference to a constant
int a = 2, b = 3;
int &j = a + b;//invalid:reference to a temporary

    使用右值引用后,下面的代碼完全合法:

int &&i = 2;
int a = 2, b = 3;
int &&j = a + b;

    前面示例中單獨(dú)使用右值引用的情況很少見。


向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI