外國(guó)大氣網(wǎng)站設(shè)計(jì)谷歌首頁(yè)
C++引用深度詳解
- 前言
- 1. 引用的本質(zhì)與核心特性
- 1.1 引用概念
- 1.2 核心特性
- 2. 常引用與權(quán)限控制
- 2.1 權(quán)限傳遞規(guī)則
- 2.2 常量引用
- 2.3 臨時(shí)變量保護(hù)
- 1. 樣例
- 2. 樣例
- 3. 測(cè)試
- 三、引用使用場(chǎng)景分析
- 3.1 函數(shù)參數(shù)傳遞
- 輸出型參數(shù)
- 避免多級(jí)指針
- 高效傳參
- 3.2 做函數(shù)返回值
- 正確使用
- 危險(xiǎn)案例
- 4. 性能對(duì)比實(shí)驗(yàn)
- 4.1 參數(shù)傳遞效率
- 4.2 返回效率對(duì)比
- 5. 引用與指針的終極對(duì)比
- 5.1 底層實(shí)現(xiàn)
- 5.2 特性對(duì)比表
- 6. 高級(jí)應(yīng)用技巧
- 6.1 鏈?zhǔn)讲僮?/li>
- 7. 總結(jié)引用要點(diǎn)
- 8. 最佳實(shí)踐指南
前言
本文深度探索引用的各種用法和特性。介紹引用的語(yǔ)法,核心特性,引用的權(quán)限控制,常引用以及引用的各種使用場(chǎng)景。
1. 引用的本質(zhì)與核心特性
1.1 引用概念
引用(Reference)是C++引入的重要特性,是 C++ 中的一種數(shù)據(jù)類型。
從語(yǔ)法層面講,引用是變量的別名。與指針不同,引用在語(yǔ)法層面不開(kāi)辟新空間,而是與原變量共享內(nèi)存地址。
引用不會(huì)創(chuàng)建新的對(duì)象,只是創(chuàng)建另一個(gè)訪問(wèn)現(xiàn)有對(duì)象的方式,引用類型變量是已有變量的別名。
引用在語(yǔ)法上與指針類似,但其語(yǔ)義和使用方式不同。
我們創(chuàng)建一個(gè)變量,其實(shí)就是對(duì)一塊內(nèi)存空間
取名字
而創(chuàng)建引用類型對(duì)象,就是對(duì)已有的一塊空間取第二個(gè)名字。
兩個(gè)名字代表的是同一塊空間。
int main() {int a = 10;int& ra = a; // ra是a的別名ra = 20; // 修改ra等同于修改acout << a; // 輸出20
}
可以看到:
- 對(duì)ra進(jìn)行操作,也就是對(duì)a進(jìn)行操作。
- 變量
ra
和ra
具有相同的地址。
1.2 核心特性
特性 | 說(shuō)明 | 示例驗(yàn)證 |
---|---|---|
必須初始化 | 定義時(shí)必須綁定實(shí)體 | int& r; 編譯錯(cuò)誤 |
不可重綁定 | 綁定后不能指向其他變量 | int b=20; ra=b; 實(shí)為賦值 |
類型嚴(yán)格匹配 | 必須與實(shí)體類型一致 | double d=1.1; int& rd=d; 錯(cuò)誤 |
多級(jí)別名支持 | 可對(duì)引用再次引用 | int& rra=ra; 合法 |
int main() {int a = 666;int num = 100;int& b = a;int& c = b; //可對(duì)引用再次引用int& d = c; //可對(duì)引用再次引用//int& e; //引用必須初始化,該語(yǔ)句編譯會(huì)報(bào)錯(cuò)。cout << d << endl;d = num; //引用一旦指定,不可修改 所以這里是 賦值, 是把num的值 100 賦值給 d cout << &a << endl; //輸出的地址相同cout << &b << endl;cout << &c << endl;cout << &d << endl;cout << a << endl; //輸出的值相同cout << b << endl;cout << c << endl;cout << d << endl;
}
- 引用必須初始化
- 引用一旦指定,不可重綁定
- 引用的類型嚴(yán)格匹配
- 一個(gè)變量可以有多個(gè)引用(多個(gè)別名),引用變量也可以有引用(引用的別名)。
- 在語(yǔ)法層面上, 我們認(rèn)為 引用沒(méi)有開(kāi)辟新空間, 只是對(duì)同一片內(nèi)存空間取了多個(gè)名字
2. 常引用與權(quán)限控制
2.1 權(quán)限傳遞規(guī)則
操作 | 合法性 | 說(shuō)明 |
---|---|---|
變量 → 常引用 | ?? | 權(quán)限縮小 |
常量 → 非 常引用 | ? | 權(quán)限放大 |
常引用 → 常引用 | ?? | 權(quán)限不變 |
注意事項(xiàng):
- 權(quán)限可以平移。
- 權(quán)限可以縮小。
- 權(quán)限不能放大。
看如下,此處有報(bào)錯(cuò),為什么?
1. 首先聲明,每個(gè)變量名都有其相應(yīng)的權(quán)限。
2. 也就是說(shuō),每塊內(nèi)存,都有相應(yīng)的權(quán)限。
3. 引用,就是對(duì)一塊內(nèi)存起了別名
int x = 0
, 創(chuàng)建變量xint& y = x
, y是x的引用。此處, y是int型的引用,發(fā)生了權(quán)限的平移。const int& z = x
, 此處發(fā)生了權(quán)限的縮小。該內(nèi)存塊在使用名字z
時(shí),權(quán)限為const,不能修改。- 名字x和y權(quán)限相同,即, 該內(nèi)存塊在使用名字
x
和y
時(shí),可以修改 - 因此
++x
正確,++z
會(huì)報(bào)錯(cuò)。
2.2 常量引用
int main() {const int a = 10;//int& ra = a; //編譯出錯(cuò),因?yàn)?a為常量const int& ra = a; //正確寫(xiě)法//int& b = 10; //編譯出錯(cuò),因?yàn)?10 為常量, 該語(yǔ)句產(chǎn)生了權(quán)限的放大const int& b = 10; //正確寫(xiě)法return 0;
}
- 對(duì)
const int a = 10;
, 有int& ra = a
, 編譯出錯(cuò),因?yàn)?a為常量, 該語(yǔ)句發(fā)生了權(quán)限的放大
2.3 臨時(shí)變量保護(hù)
1. 樣例
聲明1:在C/C++中,只要發(fā)生類型轉(zhuǎn)換,就會(huì)產(chǎn)生臨時(shí)變量。
聲明2:臨時(shí)變量具有常性(不能修改)。
類型轉(zhuǎn)換時(shí)會(huì)產(chǎn)生具有常性的臨時(shí)變量,看以下例子:
double d = 12.34;//int& rd = d; //編譯出錯(cuò),因?yàn)?類型不同const int& rd = d; // 合法,等價(jià)于:// int temp = d; // d為3.14, 常量// const int& rd = temp;
以上過(guò)程如下:
- 引用時(shí)發(fā)生類型轉(zhuǎn)換,實(shí)質(zhì)上是對(duì)臨時(shí)變量的引用。
- 臨時(shí)變量具有常性。
double d = 12.34
,//int& rd = d; //編譯出錯(cuò)
。臨時(shí)變量具有常性
,實(shí)質(zhì)上可以理解為:int& rd = const temp
發(fā)生了權(quán)限的放大,因此報(bào)錯(cuò)。 - 臨時(shí)變量具有常性。
const int& rd = d
, 實(shí)質(zhì)上可以理解為:const int& rd = const d
, 是權(quán)限的轉(zhuǎn)移。因此正確。
2. 樣例
聲明3:函數(shù)在進(jìn)行值返回時(shí),返回的同樣是臨時(shí)變量。該臨時(shí)變量是原函數(shù)的拷貝。
實(shí)際上返回的是具有常性的臨時(shí)變量
。
清楚了這一點(diǎn)后,以下例子的原理同上。
//例子
int func1() { //返回x的拷貝,會(huì)產(chǎn)生臨時(shí)變量static int x = 10;return x;
}int& func2() { //返回x的別名, 不會(huì)產(chǎn)生臨時(shí)變量static int x = 10;return x;
}
int main() {//int& x = func1(); //權(quán)限放大,錯(cuò)誤。int x1 = func1(); // 僅拷貝const int& y = func1(); //權(quán)限平移,可以進(jìn)行int& ret2 = func2(); //可以,權(quán)限的平移 const int& ret2_ = func2(); //可以,權(quán)限的縮小//總結(jié),func返回的是一個(gè)變量的別名, return 0;
}
3. 測(cè)試
//測(cè)試類型轉(zhuǎn)換時(shí)會(huì)產(chǎn)生臨時(shí)變量
int main() {int i = 10;double j = 10.11;//過(guò)程:double temp = i; double j = temp//該過(guò)程會(huì)發(fā)生類型提升//一般是小的往大的進(jìn)行類型提升,提升的時(shí)候不能改變?cè)兞俊?/span>//因此只能產(chǎn)生原變量的副本,即臨時(shí)變量if (j > i) //此處是 double j 和 double i的比較cout << "xxxxxxxxxxxxx" << endl;return 0;
}
運(yùn)行結(jié)果如下:
三、引用使用場(chǎng)景分析
3.1 函數(shù)參數(shù)傳遞
輸出型參數(shù)
//利用引用,可以避免指針和多級(jí)指針
void Swap(int& a, int& b) { //交換值 形參是實(shí)參的別名int temp = a;a = b;b = temp;
}
避免多級(jí)指針
void Swap(int*& a, int*& b) { //交換指針 如果不用引用,交換指針變量需要用二級(jí)指針int* temp = a;a = b;b = temp;
}
高效傳參
struct BigData { int arr[10000]; };// 值傳遞:拷貝4w字節(jié)
void ProcessData(BigData data); // 引用傳遞:僅傳地址(4 或 8字節(jié))
void ProcessDataOpt(const BigData& data);
3.2 做函數(shù)返回值
正確使用
int& GetStatic() {static int count = 0;return count; // 靜態(tài)變量, 生命周期足夠
}
危險(xiǎn)案例
int& DangerousRet() {int local = 10;return local; // 返回局部變量引用!
}
- 如果函數(shù)返回時(shí),出了函數(shù)作用域,如果返回對(duì)象還在(還沒(méi)還給系統(tǒng)),則可以使用引用返回。
- 如果已經(jīng)還給系統(tǒng)了,則必須使用傳值返回。
不能返回局部對(duì)象(變量)的引用。
4. 性能對(duì)比實(shí)驗(yàn)
4.1 參數(shù)傳遞效率
struct HugeStruct { int data[10000]; };void ValueFunc(HugeStruct hs) {} // 值傳遞
void RefFunc(const HugeStruct& hs) {} // 引用傳遞// 測(cè)試結(jié)果(10000次調(diào)用):
// 值傳遞耗時(shí):1587ms
// 引用傳遞耗時(shí):2ms
4.2 返回效率對(duì)比
HugeStruct g_data;HugeStruct ReturnByValue() { return g_data; }
HugeStruct& ReturnByRef() { return g_data; }// 測(cè)試結(jié)果(100000次調(diào)用):
// 值返回耗時(shí):3521ms
// 引用返回耗時(shí):1ms
5. 引用與指針的終極對(duì)比
5.1 底層實(shí)現(xiàn)
; 引用實(shí)現(xiàn)
mov dword ptr [a], 0Ah
lea eax, [a] ; 取地址
mov dword ptr [ra], eax ; 指針實(shí)現(xiàn)
mov dword ptr [a], 0Ah
lea eax, [a]
mov dword ptr [pa], eax
關(guān)鍵區(qū)別
- 引用:在 C++ 中引用通常會(huì)被優(yōu)化為指針,底層是通過(guò)地址訪問(wèn),但語(yǔ)法上沒(méi)有指針的顯式解引用和取地址操作。
- 指針:指針顯式地存儲(chǔ)內(nèi)存地址,允許進(jìn)行指針?biāo)阈g(shù)操作,指針本身也可以為空(
nullptr
)。
從底層來(lái)看,引用和指針的實(shí)現(xiàn)非常相似,都是通過(guò)存儲(chǔ)地址來(lái)實(shí)現(xiàn)對(duì)變量的間接訪問(wèn)。區(qū)別在于語(yǔ)法和語(yǔ)義上,引用在 C++ 中看起來(lái)更像是變量的別名,而指針則顯式地表示地址。
5.2 特性對(duì)比表
特性 | 引用 | 指針 |
---|---|---|
初始化要求 | 必須 | 可選 |
空值 | 無(wú)NULL引用 | 支持NULL |
重定向 | 不可 | 可以 |
訪問(wèn)方式 | 直接訪問(wèn) | 需解引用(*或->) |
類型安全 | 更高 | 較低 |
多級(jí)間接 | 單級(jí) | 支持多級(jí) |
sizeof | 返回原類型大小 | 返回地址大小(4或8字節(jié)) |
6. 高級(jí)應(yīng)用技巧
6.1 鏈?zhǔn)讲僮?/h3>
struct Matrix {Matrix& Transpose() { /*...*/ return *this; }Matrix& Rotate(double angle) { /*...*/ return *this; }
};Matrix mat;
mat.Transpose().Rotate(45); // 鏈?zhǔn)秸{(diào)用
7. 總結(jié)引用要點(diǎn)
- 引用概念上定義一個(gè)變量的別名,指針存儲(chǔ)一個(gè)變量地址。
- 引用在定義時(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ì)更安全
8. 最佳實(shí)踐指南
- 優(yōu)先const引用:函數(shù)參數(shù)盡量使用
const T&
形式 - 警惕返回引用:確保返回對(duì)象生命周期足夠
- 替代輸出參數(shù):用引用替代指針作為輸出參數(shù)
- 類型轉(zhuǎn)換注意:隱式轉(zhuǎn)換產(chǎn)生臨時(shí)變量需用const引用
- 與智能指針配合:
std::shared_ptr<T>&
管理資源(后續(xù)講解)
struct Matrix {Matrix& Transpose() { /*...*/ return *this; }Matrix& Rotate(double angle) { /*...*/ return *this; }
};Matrix mat;
mat.Transpose().Rotate(45); // 鏈?zhǔn)秸{(diào)用
const T&
形式std::shared_ptr<T>&
管理資源(后續(xù)講解)以上就是關(guān)于引用的所有內(nèi)容了,碼字整理不易,歡迎各位大佬在評(píng)論區(qū)交流