溫馨提示×

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

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

C++引用如何使用

發(fā)布時(shí)間:2022-05-17 09:18:39 來(lái)源:億速云 閱讀:121 作者:iii 欄目:開(kāi)發(fā)技術(shù)

這篇文章主要介紹“C++引用如何使用”的相關(guān)知識(shí),小編通過(guò)實(shí)際案例向大家展示操作過(guò)程,操作方法簡(jiǎn)單快捷,實(shí)用性強(qiáng),希望這篇“C++引用如何使用”文章能幫助大家解決問(wèn)題。

    一. 引用的概念

    引用不是新定義一個(gè)變量,而是給已存在變量取了一個(gè)別名,編譯器不會(huì)為引用變量開(kāi)辟內(nèi)存空間,它和它引用的變量共用同一塊內(nèi)存空間。

    類型& 引用變量名(對(duì)象名) = 引用實(shí)體;

    如下:

    void TestRef()
    {
         int a = 10;
         int& ra = a;//<====定義引用類型
         printf("%p\n", &a);
         printf("%p\n", &ra);
    }

    注意:引用類型必須和引用實(shí)體是同種類型的

    二. 引用特性

    1. 引用在定義時(shí)必須初始化

    2. 一個(gè)變量可以有多個(gè)引用

    3. 引用一旦引用一個(gè)實(shí)體,再不能引用其他實(shí)體

    如下:

    void TestRef()
    {
         int a = 10;
         int a2 = 20;
         //a的多個(gè)引用
         int& b = a;
         int& c = a;
         int& d = b;
         int& ra;//該條語(yǔ)句編譯時(shí)會(huì)出錯(cuò),未初始化
         int &ra = a2;//報(bào)錯(cuò),引用了其他實(shí)體
         printf("%p %p %p %p\n", &a, &b, &c, &d); 
    }

    C++引用如何使用

    三. 常引用

    void TestConstRef()
    {
         const int a = 10;
         //int& ra = a; // 該語(yǔ)句編譯時(shí)會(huì)出錯(cuò),a為常量
         const int& ra = a;
         // int& b = 10; // 該語(yǔ)句編譯時(shí)會(huì)出錯(cuò),b為常量
         const int& b = 10;
         double d = 12.34;
         //int& rd = d; // 該語(yǔ)句編譯時(shí)會(huì)出錯(cuò),類型不同
         const int& rd = d;
         //int& c = 100; // 該語(yǔ)句編譯時(shí)會(huì)出錯(cuò),常量是只讀的
         const int& c = 100;
    }

    注意:

    引用取別名原則:對(duì)原引用變量,讀寫(xiě)權(quán)限只能縮小,不能放大

    const int a = 10;

    int& ra = a;

    編譯不通過(guò),因?yàn)榉糯罅藱?quán)限,原引用本來(lái)是只讀,但是引用以后卻變成了可讀可寫(xiě)

    int& b = 10;

    const int& b = 10;

    編譯可以通過(guò),因?yàn)榭s小了權(quán)限,原引用本來(lái)是可讀可寫(xiě),引用后變成了只讀

    double d = 12.34;

    int& rd = d;

    編譯不通過(guò),這里比較特殊,看起來(lái)是因?yàn)轭愋筒煌鴪?bào)錯(cuò),其實(shí)不然,報(bào)錯(cuò)是因?yàn)闄?quán)限放大了,為什么?

    int類型要引用double類型,double類型轉(zhuǎn)化到int類型屬于隱式類型轉(zhuǎn)換會(huì)舍棄小數(shù)位,隱式類型轉(zhuǎn)換會(huì)產(chǎn)生臨時(shí)變量,double類型到int類型會(huì)創(chuàng)建一個(gè)臨時(shí)變量存儲(chǔ)double變成了int類型的值,這里需要注意,這個(gè)臨時(shí)變量具有常性是只讀的,rd其實(shí)是引用了這個(gè)臨時(shí)變量,因?yàn)榕R時(shí)變量是只讀的,引用了臨時(shí)變量的rd也應(yīng)該是只讀的,所以這就是為什么const int& rd = d 可以編譯通過(guò)。

    int& c = 100;

    編譯通過(guò),因?yàn)槌A勘緛?lái)就是只讀的,不加const代表引用后變成了可讀可寫(xiě),權(quán)限放大。

    四. 使用場(chǎng)景

    1. 做參數(shù)

    void Swap(int& left, int& right)
    {
         int temp = left;
         left = right;
         right = temp;
    }
    • 輸出型參數(shù)

    • 減少拷貝,提高效率

    2. 做返回值

    int& Count()
    {
         static int n = 0;
         n++;
         // ...
         return n;
    }

    減少拷貝

    (傳值返回需要拷貝數(shù)據(jù),傳引用返回直接返回變量的別名)

    3. 做返回值需要注意的問(wèn)題

    首先,我們要知道當(dāng)函數(shù)返回一個(gè)值時(shí),會(huì)生成一個(gè)臨時(shí)變量,而函數(shù)的返回類型就是這個(gè)臨時(shí)變量的類型

    int Add(int a, int b)
    {
        return a + b;
    }
    int Count()
    {
        static int n = 0;
        n++;
        return n;
    }
    int main()
    {
        int temp = Add(2, 3);
        int tmp = Count();
        return 0;
    }

    以上代碼將a+b(n)的值賦值給臨時(shí)變量,臨時(shí)變量再賦值給temp(tmp),為什么要設(shè)置這個(gè)臨時(shí)變量?

    其實(shí)很簡(jiǎn)單,在這個(gè)代碼里是會(huì)有問(wèn)題的,出了函數(shù)作用域a+b的值就已經(jīng)被銷毀了,需要一個(gè)臨時(shí)變量去儲(chǔ)存這個(gè)返回值,再去訪問(wèn)那塊空間是非法的,而被static修飾的n由于它的生命周期變長(zhǎng)了,即使出了函數(shù)也不會(huì)被銷毀

    那么問(wèn)題來(lái)了,以下代碼是正確的嗎?

    int& Add(int a, int b)
    {
         int c = a + b;
         return c;
    }
    int main()
    {
         int& ret = Add(1, 2);
         return 0;
    }

    很明顯是有問(wèn)題的!這里將c的引用返回 ,而一旦出了函數(shù)c就被銷毀了,這塊空間也被操作系統(tǒng)收回,再將c的引用賦值給ret就變成了非法訪問(wèn)了,就變成了由引用造成的野指針

    由上面的問(wèn)題可以衍生出以下代碼:

    這里的ret是什么?

    int& Add(int a, int b)
    {
         int c = a + b;
         return c;
    }
    int main()
    {
         int& ret = Add(1, 2);
         Add(3, 4);
         cout << "Add(1, 2) is :"<< ret <<endl;
         return 0;
    }

    很明顯是7,ret是c的引用,由于出了函數(shù)以后這塊空間的使用權(quán)還給了操作系統(tǒng),由于第二次函數(shù)調(diào)用仍然是在第一次函數(shù)調(diào)用的空間進(jìn)行棧幀的建立,因?yàn)閞et的地址(ret的地址就是之前那塊臨時(shí)變量的地址)還是之前那個(gè)地址,所以由于第二次返回c時(shí)建立的臨時(shí)變量已經(jīng)變成了7,所以ret也變成了7

    但是一定會(huì)是7嗎?其實(shí)不然,我們知道這塊空間的使用權(quán)還給了操作系統(tǒng),這塊空間也有可能會(huì)被其他程序使用了,導(dǎo)致數(shù)值變成了不確定性,因?yàn)檫@里是直接馬上又調(diào)用了這個(gè)函數(shù),所以會(huì)是7,所以,其實(shí)正確答案應(yīng)該是隨機(jī)值才對(duì)

    看下面這個(gè)代碼就是典型的例子:

    int& Add(int a, int b)
    {
         int c = a + b;
         return c;
    }
    int main()
    {
         int& ret = Add(1, 2);
         Add(3, 4);
         cout << "Add(1, 2) is :"<< ret <<endl;
         cout << "Add(1, 2) is :"<< ret <<endl;
         return 0;
    }

    這里的輸出語(yǔ)句其實(shí)也是調(diào)用了函數(shù),由上面可知第一個(gè)是7,那么第二個(gè)呢?隨機(jī)值!因?yàn)檫M(jìn)行了第一次輸出后其實(shí)也是進(jìn)行了函數(shù)調(diào)用,函數(shù)調(diào)用會(huì)建立棧幀,在上一個(gè)輸出建立的棧幀處重新建立了棧幀,函數(shù)調(diào)用前需要先傳參,由于上一個(gè)輸出語(yǔ)句銷毀完棧幀以后ret地址處的值被覆蓋成隨機(jī)值,在第二次輸出語(yǔ)句中此時(shí)就會(huì)把這個(gè)隨機(jī)值作為參數(shù)傳過(guò)去給函數(shù),導(dǎo)致輸出了隨機(jī)值,所以傳引用返回不是所有情況都可以使用的,像一開(kāi)始加上了static關(guān)鍵字之類的才可以返回,因?yàn)閚的生命周期變長(zhǎng)了,出了函數(shù)作用域沒(méi)有被銷毀,取值都是去靜態(tài)區(qū)取數(shù)據(jù)。

    結(jié)論:如果函數(shù)返回時(shí),出了函數(shù)作用域,如果返回對(duì)象還未還給系統(tǒng),則可以使用引用返回,如果已經(jīng)還給系統(tǒng)了,則必須使用傳值返回。

    五. 傳值傳引用效率對(duì)比

    以值作為參數(shù)或者返回值類型,在傳參和返回期間,函數(shù)不會(huì)直接傳遞實(shí)參或者將變量本身直接返回,而是傳遞實(shí)參或者返回變量的一份臨時(shí)的拷貝,因此用值作為參數(shù)或者返回值類型,效率是非常低下的,尤其是當(dāng)參數(shù)或者返回值類型非常大時(shí),效率就更低。

    1. 值和引用傳參時(shí)的效率比較

    #include <time.h>
    struct A { 
    	int a[10000]; 
    };
    void TestFunc1(A a) {}
    void TestFunc2(A& a) {}
    void TestFunc3(A* a) {}
    void TestRefAndValue()
    {
    	A a;
    	// 以值作為函數(shù)參數(shù)
    	size_t begin1 = clock();
    	for (size_t i = 0; i < 10000; ++i)
    		TestFunc1(a);
    	size_t end1 = clock();
    	// 以引用作為函數(shù)參數(shù)
    	size_t begin2 = clock();
    	for (size_t i = 0; i < 10000; ++i)
    		TestFunc2(a);
    	size_t end2 = clock();
    	// 以指針作為參數(shù)
    	size_t begin3 = clock();
    	for (size_t i = 0; i < 10000; ++i)
    		TestFunc3(&a);
    	size_t end3 = clock();
    	// 分別計(jì)算兩個(gè)函數(shù)運(yùn)行結(jié)束后的時(shí)間
    	cout << "TestFunc1(A)-time:" << end1 - begin1 << endl;
    	cout << "TestFunc2(A&)-time:" << end2 - begin2 << endl;
    	cout << "TestFunc2(A&)-time:" << end3 - begin3 << endl;
    }

    C++引用如何使用

    2. 值和引用的作為返回值類型的性能比較

    #include <time.h>
    struct A{ int a[10000]; };
    A a;
    // 值返回
    A TestFunc1() { return a;}
    // 引用返回
    A& TestFunc2(){ return a;}
    void TestReturnByRefOrValue()
    {
         // 以值作為函數(shù)的返回值類型
         size_t begin1 = clock();
         for (size_t i = 0; i < 100000; ++i)
         TestFunc1();
         size_t end1 = clock();
         // 以引用作為函數(shù)的返回值類型
         size_t begin2 = clock();
         for (size_t i = 0; i < 100000; ++i)
         TestFunc2();
         size_t end2 = clock();
         // 計(jì)算兩個(gè)函數(shù)運(yùn)算完成之后的時(shí)間
         cout << "TestFunc1 time:" << end1 - begin1 << endl;
         cout << "TestFunc2 time:" << end2 - begin2 << endl;
    }

    C++引用如何使用

    通過(guò)上述代碼的比較,發(fā)現(xiàn)傳值和指針在作為傳參以及返回值類型上效率相差很大。

    六. 引用和指針

    在語(yǔ)法概念上引用就是一個(gè)別名,沒(méi)有獨(dú)立空間,和其引用實(shí)體共用同一塊空間。

    int main()
    {
         int a = 10;
         int& ra = a;
         cout<<"&a = "<<&a<<endl;
         cout<<"&ra = "<<&ra<<endl;
         return 0;
    }

    在底層實(shí)現(xiàn)上實(shí)際是有空間的,因?yàn)橐檬前凑罩羔樂(lè)绞絹?lái)實(shí)現(xiàn)的。

    int main()
    {
         int a = 10;
         int& ra = a;
         ra = 20;
         int* pa = &a;
         *pa = 20;
         return 0;
    }

    我們來(lái)看下引用和指針的匯編代碼對(duì)比:

    引用和指針的不同點(diǎn):

    • 引用在定義時(shí)必須初始化,指針沒(méi)有要求(建議初始化)

    • 引用在初始化時(shí)引用一個(gè)實(shí)體后,就不能再引用其他實(shí)體,而指針可以在任何時(shí)候指向任何一個(gè)同類型實(shí)體

    • 沒(méi)有NULL引用,但有NULL指針

    • 在sizeof中含義不同:引用結(jié)果為引用類型的大小,但指針始終是地址空間所占字節(jié)個(gè)數(shù)(32位平臺(tái)下占 4個(gè)字節(jié))

    • 引用自加即引用的實(shí)體增加1,指針自加即指針向后偏移一個(gè)類型的大小

    • 有多級(jí)指針,但是沒(méi)有多級(jí)引用

    • 訪問(wèn)實(shí)體方式不同,指針需要顯式解引用,引用編譯器自己處理

    • 引用比指針使用起來(lái)相對(duì)更安全

    引用和指針的相同點(diǎn):

    雖然從語(yǔ)法角度來(lái)看引用是別名沒(méi)有額外開(kāi)空間,但是底層角度來(lái)看他們是一樣的。

    什么是底層角度呢?就是通過(guò)編譯器處理的結(jié)果來(lái)看,以下是指針和引用經(jīng)編譯器處理后的結(jié)果

    C++引用如何使用

    C++引用如何使用

    我們會(huì)發(fā)現(xiàn)匯編指令是一致的,這就說(shuō)明了從底層角度看這兩個(gè)實(shí)現(xiàn)方式是一樣的

    關(guān)于“C++引用如何使用”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識(shí),可以關(guān)注億速云行業(yè)資訊頻道,小編每天都會(huì)為大家更新不同的知識(shí)點(diǎn)。

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

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

    c++
    AI