欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

C++深入探究引用的使用

 更新時間:2022年05月16日 15:32:26   作者:Hiland.  
引用是C++一個很重要的特性,顧名思義是某一個變量或?qū)ο蟮膭e名,對引用的操作與對其所綁定的變量或?qū)ο蟮牟僮魍耆葍r,這篇文章主要給大家總結(jié)介紹了C++中引用的相關(guān)知識點,需要的朋友可以參考下

一. 引用的概念

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

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

如下:

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

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

二. 引用特性

1. 引用在定義時必須初始化

2. 一個變量可以有多個引用

3. 引用一旦引用一個實體,再不能引用其他實體

如下:

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

三. 常引用

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

注意:

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

const int a = 10;

int& ra = a;

編譯不通過,因為放大了權(quán)限,原引用本來是只讀,但是引用以后卻變成了可讀可寫

int& b = 10;

const int& b = 10;

編譯可以通過,因為縮小了權(quán)限,原引用本來是可讀可寫,引用后變成了只讀

double d = 12.34;

int& rd = d;

編譯不通過,這里比較特殊,看起來是因為類型不同而報錯,其實不然,報錯是因為權(quán)限放大了,為什么?

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

int& c = 100;

編譯通過,因為常量本來就是只讀的,不加const代表引用后變成了可讀可寫,權(quán)限放大。

四. 使用場景

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. 做返回值需要注意的問題

首先,我們要知道當(dāng)函數(shù)返回一個值時,會生成一個臨時變量,而函數(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)的值賦值給臨時變量,臨時變量再賦值給temp(tmp),為什么要設(shè)置這個臨時變量?

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

那么問題來了,以下代碼是正確的嗎?

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

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

由上面的問題可以衍生出以下代碼:

這里的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)用的空間進行棧幀的建立,因為ret的地址(ret的地址就是之前那塊臨時變量的地址)還是之前那個地址,所以由于第二次返回c時建立的臨時變量已經(jīng)變成了7,所以ret也變成了7

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

看下面這個代碼就是典型的例子:

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;
}

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

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

五. 傳值傳引用效率對比

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

1. 值和引用傳參時的效率比較

#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();
	// 分別計算兩個函數(shù)運行結(jié)束后的時間
	cout << "TestFunc1(A)-time:" << end1 - begin1 << endl;
	cout << "TestFunc2(A&)-time:" << end2 - begin2 << endl;
	cout << "TestFunc2(A&)-time:" << end3 - begin3 << endl;
}

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();
     // 計算兩個函數(shù)運算完成之后的時間
     cout << "TestFunc1 time:" << end1 - begin1 << endl;
     cout << "TestFunc2 time:" << end2 - begin2 << endl;
}

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

六. 引用和指針

在語法概念上引用就是一個別名,沒有獨立空間,和其引用實體共用同一塊空間。

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

在底層實現(xiàn)上實際是有空間的,因為引用是按照指針方式來實現(xiàn)的。

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

我們來看下引用和指針的匯編代碼對比:

引用和指針的不同點:

  • 引用在定義時必須初始化,指針沒有要求(建議初始化)
  • 引用在初始化時引用一個實體后,就不能再引用其他實體,而指針可以在任何時候指向任何一個同類型實體
  • 沒有NULL引用,但有NULL指針
  • 在sizeof中含義不同:引用結(jié)果為引用類型的大小,但指針始終是地址空間所占字節(jié)個數(shù)(32位平臺下占 4個字節(jié))
  • 引用自加即引用的實體增加1,指針自加即指針向后偏移一個類型的大小
  • 有多級指針,但是沒有多級引用
  • 訪問實體方式不同,指針需要顯式解引用,引用編譯器自己處理
  • 引用比指針使用起來相對更安全

引用和指針的相同點:

雖然從語法角度來看引用是別名沒有額外開空間,但是底層角度來看他們是一樣的。

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

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

到此這篇關(guān)于C++深入探究引用的使用的文章就介紹到這了,更多相關(guān)C++引用內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • C語言 不使用strcat函數(shù)實現(xiàn)連接兩個字符串功能代碼

    C語言 不使用strcat函數(shù)實現(xiàn)連接兩個字符串功能代碼

    今天小編就為大家分享一篇C語言 不使用strcat函數(shù)實現(xiàn)連接兩個字符串功能代碼,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2019-12-12
  • 詳解C++中string的用法和例子

    詳解C++中string的用法和例子

    string是C++標(biāo)準(zhǔn)庫的一個重要的部分,主要用于字符串處理。這篇文章主要介紹了C++ string的用法和例子,需要的朋友可以參考下
    2018-05-05
  • C++ OpenCV制作哈哈鏡圖像效果

    C++ OpenCV制作哈哈鏡圖像效果

    這篇文章主要介紹了使用OpenCV C++ 制作哈哈鏡圖像特效。其原理就是讓圖像像素扭曲,將像素重新進行映射。感興趣的可以跟隨小編一起試一試
    2022-01-01
  • C語言動態(tài)內(nèi)存管理malloc柔性數(shù)組示例詳解

    C語言動態(tài)內(nèi)存管理malloc柔性數(shù)組示例詳解

    這篇文章主要為大家介紹了C語言動態(tài)內(nèi)存管理malloc柔性數(shù)組示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-10-10
  • 淺談C++中字符串輸入get與getline的區(qū)別

    淺談C++中字符串輸入get與getline的區(qū)別

    這篇文章主要介紹了C++中字符串輸入get與getline的區(qū)別,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-03-03
  • Qt如何通過pos()獲取坐標(biāo)信息

    Qt如何通過pos()獲取坐標(biāo)信息

    這篇文章主要給大家介紹了關(guān)于Qt如何通過pos()獲取坐標(biāo)信息的相關(guān)資料,文中通過代碼介紹的非常詳細(xì),對大家學(xué)習(xí)或者使用qt具有一定的參考借鑒價值,需要的朋友可以參考下
    2024-01-01
  • 關(guān)于C++為什么不加入垃圾回收機制解析

    關(guān)于C++為什么不加入垃圾回收機制解析

    C++為什么不加入垃圾回收機制呢?現(xiàn)在肯定還有很多人不太了解,不過沒關(guān)系,下面小編就為大家詳細(xì)的介紹下究竟C++為什么不加入垃圾回收機制。一起跟隨小編過來看看吧
    2017-01-01
  • C++?Boost?MultiArray簡化使用多維數(shù)組庫

    C++?Boost?MultiArray簡化使用多維數(shù)組庫

    Boost是為C++語言標(biāo)準(zhǔn)庫提供擴展的一些C++程序庫的總稱。Boost庫是一個可移植、提供源代碼的C++庫,作為標(biāo)準(zhǔn)庫的后備,是C++標(biāo)準(zhǔn)化進程的開發(fā)引擎之一,是為C++語言標(biāo)準(zhǔn)庫提供擴展的一些C++程序庫的總稱
    2022-11-11
  • C++利用代理模式實現(xiàn)遠(yuǎn)程代理,虛擬代理和保護代理

    C++利用代理模式實現(xiàn)遠(yuǎn)程代理,虛擬代理和保護代理

    今天給大家簡單介紹代理模式,一個很簡單的設(shè)計模式,旨在不改變原對象的情況下通過代理對象來控制對原對象的訪問。代理模式根據(jù)具體情況還可以分為遠(yuǎn)程代理、虛擬代理、保護代理等,下面來介紹一下
    2023-04-04
  • 詳解C語言中for循環(huán)與while循環(huán)的用法

    詳解C語言中for循環(huán)與while循環(huán)的用法

    這篇文章主要通過幾個示例為大家介紹一下C語言中for循環(huán)與while循環(huán)的用法以及二者的區(qū)別,文中的代碼講解詳細(xì),對我們學(xué)習(xí)C語言有一定幫助,需要的可以參考一下
    2022-07-07

最新評論