專欄放在【C++知識(shí)總結(jié)】,會(huì)持續(xù)更新,期待支持??

引用
引用的概念
在C++中,引用的本質(zhì)其實(shí)就是給一個(gè)已經(jīng)存在的變量”起別名“。也就是說,引用與它所引用的對象共用一塊空間。(同一塊空間的多個(gè)名字)
就比如說,李逵又叫黑旋風(fēng),而黑旋風(fēng)就是指李逵本人,只是名字換了而已。

引用的特性
1. 引用在定義時(shí)必須初始化
2. 一個(gè)變量可以有多個(gè)引用,但一個(gè)引用只能有一個(gè)實(shí)體對象
#include<iostream>
using namespace std;
int main()
{
int a = 0;
//一個(gè)變量可以有多個(gè)引用,但一個(gè)引用只能有一個(gè)實(shí)體
int& b = a;//不可以寫成int& b; 引用必須在定義時(shí)初始化
int& c = b;
int& d = c;
cout << &a << endl;//012FFBD0
cout << &b << endl;//012FFBD0
cout << &c << endl;//012FFBD0
cout << &d << endl;//012FFBD0
//地址相同,abcd共用同一塊空間
// 另外,引用類型與引用實(shí)體的類型必須一致,這里不能寫為char & d = a(error)
return 0;
}
擴(kuò)展(函數(shù)棧幀的創(chuàng)建與銷毀)
這里我們進(jìn)行復(fù)習(xí)一下關(guān)于函數(shù)棧幀的一些知識(shí)。我們知道,在調(diào)用一個(gè)函數(shù)時(shí), 首先會(huì)在內(nèi)存占用一塊空間,用來創(chuàng)建該函數(shù)的函數(shù)棧幀,當(dāng)調(diào)用結(jié)束后,該函數(shù)棧幀會(huì)被銷毀,這里需要注意的是,當(dāng) 棧幀被銷毀后,這里的空間實(shí)際上在內(nèi)存中還是存在的,只不過空間的使用權(quán)不再歸我們使用。 并且函數(shù)棧幀的銷毀,可能會(huì)對原有空間進(jìn)行清理。

這里可以舉個(gè)例子來理解一下,就好比說我們在酒店開了一個(gè)房間,并且在退房時(shí)把我們的電腦放在了房間里,在這里,酒店就相當(dāng)于內(nèi)存的存在,而我們退房的那一刻,就好比 函數(shù)棧幀銷毀的那一刻,但是雖然我們退房了,該房間還是實(shí)際存在的,并沒有說隨著我們的退房而消失,只不過不再歸我們使用。并且房間里的東西也 可能會(huì)被清理(也可能依然還在),加入此時(shí)我們再進(jìn)行使用該房間,用是可以用,只不過肯定是不合法的,這種行為就好比 空間的非法訪問。
引用的使用場景
做參數(shù)進(jìn)行引用(輸出型參數(shù))
所謂輸出型參數(shù),實(shí)際上就是可以影響實(shí)參的參數(shù),就比如我們經(jīng)常寫的交換兩個(gè)變量的值,在以前我們會(huì)使用指針來完成傳址調(diào)用,從而實(shí)現(xiàn)形參的改變影響實(shí)參,但現(xiàn)在我們可以用引用來實(shí)現(xiàn),如下:
//做參數(shù)來使用,由于共用同一塊空間,所以這里的c實(shí)際上就是a,d實(shí)際就是b
void Swap(int& c, int& d)
{
int tmp = c;
c = d;
d = tmp;
}
int main()
{
int a = 1, b = 2;
Swap(a, b);
cout << a << " " << b << endl;//2 1
return 0;
}
可以做返回值使用
我們先來看這樣一段代碼:

這里注意的是:這里的a是局部變量,生命周期會(huì)隨著棧區(qū)的銷毀而結(jié)束,所以這里返回的實(shí)際上并不是a,我們通過查看反匯編發(fā)現(xiàn)實(shí)際上是借助了一個(gè)臨時(shí)變量來實(shí)現(xiàn)的。
那么不禁會(huì)有個(gè)疑問,假如這里的a不隨著棧幀的結(jié)束而銷毀,那么會(huì)不會(huì)直接返回a呢?可以試驗(yàn)一下:

對于這種現(xiàn)象,我們可以把引用作為返回值來使用,從而實(shí)現(xiàn)優(yōu)化,寫成如下格式:
//返回值
int& Test()
{
static int a = 10;
a++;
return a;//也會(huì)產(chǎn)生臨時(shí)變量,但是臨時(shí)變量的類型是int& 也就是a的別名,即臨時(shí)變量就是返回的a,減少了拷貝操作
}
int main()
{
int ret = Test();
return 0;
}
這就是引用返回,即在返回類型前面加上&,雖然也需要借助臨時(shí)變量的存在,但是由于臨時(shí)變量的類型為int& ,即臨時(shí)變量就是a,所以就減少了臨時(shí)變量的拷貝工作,會(huì)使效率得到提升。我們可以來驗(yàn)證一下:
傳值返回 vs 傳引用返回效率對比
#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 < 1000000; ++i)
TestFunc1();
size_t end1 = clock();
// 以引用作為函數(shù)的返回值類型
size_t begin2 = clock();
for (size_t i = 0; i < 1000000; ++i)
TestFunc2();
size_t end2 = clock();
// 計(jì)算兩個(gè)函數(shù)運(yùn)算完成之后的時(shí)間
cout << "TestFunc1 time:" << end1 - begin1 << endl;
cout << "TestFunc2 time:" << end2 - begin2 << endl;
}
int main()
{
TestReturnByRefOrValue();
}
運(yùn)行結(jié)果如下:

我們發(fā)現(xiàn)傳引用返回對比傳值返回,效率會(huì)有顯著提高(作為參數(shù)使用時(shí),傳引用參數(shù)的效率也會(huì)高于傳值作為參數(shù)的效率)
當(dāng)然,傳引用作為返回值的使用是有一定的限制的,我們發(fā)現(xiàn)上面的代碼能使用傳引用返回的原因在于,返回的變量不會(huì)隨著作用域的銷毀而銷毀。假如說返回的對象出了作用域后已經(jīng)銷毀,則必須使用傳值返回,否則返回的結(jié)果是不確定的!

并且引用用作返回時(shí),還可以修改返回對象(后面的學(xué)習(xí)會(huì)用到很多,這里簡單介紹)
如下:
#define N 10
typedef struct ARR
{
int arr[N];
}ARR;
int& PosARR(ARR& arr,int i)
{
return arr.arr[i];//這里的arr就是main函數(shù)里的arr,不會(huì)隨著PosARR函數(shù)的結(jié)束而銷毀,所以可以用引用返回
}
int main()
{
ARR arr;
for (int i = 0; i < N; i++)
{
PosARR(arr, i) = i * 10;//引用返回可以修改返回對象,這里的返回對象為arr.arr[i],對此進(jìn)行修改
}
for (int i = 0; i < N; i++)
{
cout << arr.arr[i] << " ";//0 10 20 30 40 50 60 70 80 90
}
return 0;
}
總結(jié)
引用可以用作參數(shù)來使用(輸出型參數(shù)),也可以用作返回使用,用作返回使用時(shí)返回的對象必須是出了所在函數(shù)作用域后不會(huì)銷毀的(比如static修飾的變量,全局變量,malloc......),并且引用返回時(shí),返回的對象可以被修改。同時(shí)還可以減少拷貝提高效率。
常引用
我們要記住這樣一句話:指針和引用在賦值或者初始化時(shí),權(quán)限可以被縮小或者保持,但不可進(jìn)行修改。
這是什么意思呢?通過以下代碼進(jìn)行了解:
// 權(quán)限放大(error)
//const int c = 2;//const 修飾的常量不可以進(jìn)行修改,可以理解只具有讀的屬性,不具有寫的屬性,而d可以修改,所以權(quán)限被放大
//int& d = c;//這里正確寫法應(yīng)為const int& d=c;
//const int* p1 = NULL;
//int* p2 = p1;//同上,前面加個(gè)const即可,const int* p2=p1; (√)
// 權(quán)限保持
const int c = 2;
const int& d = c;
const int* p1 =NULL;
const int* p2 = p1;
// 權(quán)限縮小
int x = 1;//x可以進(jìn)行修改,可以理解為具有讀和寫的屬性,而x是const修飾的,只具有讀的屬性,權(quán)限縮小了
const int& y = x;
int* p3 = NULL;
const int* p4 = p3;//同上
不僅如此,由于所謂臨時(shí)變量具有常性(即不可被修改)的原因,也會(huì)出現(xiàn)以下的情況:
int i = 0;
//double& p = i;//error
//由于int到double類型發(fā)生類型轉(zhuǎn)換,而類型轉(zhuǎn)換會(huì)產(chǎn)生臨時(shí)變量,臨時(shí)變量又具有常性(只可讀)
//因此在前面加上const即可
const double& p=i;//(√)
這也就解釋了上文說到的引用類型與引用實(shí)體的類型必須一致,同樣,在函數(shù)中也一樣適用:
int add(int x, int y)
{
int c = x + y;
return c;//實(shí)際上是借助臨時(shí)變量,將c拷貝給臨時(shí)變量,再將臨時(shí)變量拷貝給p
}
int main()
{
int a = 1, b = 2;
//int& p = add(a, b);//由于臨時(shí)變量具有常性的特點(diǎn),所以不可以這樣寫
//前面加上const 即可
const int& p = add(a, b);
return 0;
}
引用與指針
&是一個(gè)很熟悉的符號(hào),與指針有關(guān),用在變量前面就是取地址符號(hào),用在類型后面則為引用符號(hào),那么指針與引用之間是否有著什么關(guān)系呢?
int a=0;
int* p=&a;//&:取地址符
int& b=a;//&:引用
指針與引用的相同點(diǎn)
實(shí)際上,引用與指針,兩者之間在底層實(shí)現(xiàn)上其實(shí)是一樣的,我們可以來進(jìn)行驗(yàn)證

當(dāng)然,兩者之間也存在著很大的區(qū)別。
指針與引用的不同點(diǎn)
首先就是在語法概念上的區(qū)別, 引用只是同一個(gè)實(shí)體的不同名稱, 不會(huì)單獨(dú)開辟空間,但是指針會(huì)在內(nèi)存開辟一塊4/8byte大小的空間。
引用在定義時(shí) 必須初始化,指針沒有要求
引用在初始化時(shí)引用一個(gè)實(shí)體后,就 不能再引用其他實(shí)體,而指針可以在任何時(shí)候指向任何一個(gè)同類型實(shí)體(這一點(diǎn)也就意味著 引用并不能實(shí)現(xiàn)完全替代指針,就比如在鏈表這里,用來指向下一個(gè)節(jié)點(diǎn)的變量類型,只能是指針)
有多級指針,但是沒有多級引用
引用自加即引用的實(shí)體增加1,指針自加即指針向后偏移一個(gè)類型的大小
引用比指針使用起來相對更安全(野指針)
...
看法:
因此對于指針與引用,我們只能是說引用相較于指針來說,更加容易理解使用,并且也不會(huì)存在空引用的問題,但是在一些場景下,引用自身的特點(diǎn)(不能改變指向)也存在著使用限制,此時(shí)就得用指針來實(shí)現(xiàn)
end文章來源:http://www.zghlxwxcb.cn/news/detail-784427.html
生活原本沉悶,但跑起來就會(huì)有風(fēng)!??文章來源地址http://www.zghlxwxcb.cn/news/detail-784427.html
到了這里,關(guān)于【C++】引用與指針的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!