一.基本概念
引用即內存的別名
int a = 10;
int& b = a;
引用本身不占用內存,并非實體,對引用的所有操作都是在對目標內存進行操作
引用必須初始化,且不能更換對象
int c = 5;
b = c; // × 僅僅是在對引用的目標內存進行賦值
#include <iostream>
using namespace std;int main() {int mm1 = 20; // 1. 定義整型變量mm1并初始化為20int& mm2 = mm1; // 2. 定義整型引用mm2,綁定到mm1int mm3 = 15; // 3. 定義整型變量mm3并初始化為15mm2 = mm3; // 4. 將mm3的值賦給mm2(即mm1)cout << "mm1: " << mm1 << endl; // 輸出 15cout << "mm2: " << mm2 << endl; // 輸出 15cout << "mm3: " << mm3 << endl; // 輸出 15return 0;
}
詳細執行過程:
-
int mm1 = 20;
-
定義一個整型變量
mm1
,并將其初始值設為20
。 -
內存中:
mm1
存儲的值是20
。
-
-
int& mm2 = mm1;
-
定義一個整型引用
mm2
,并將其綁定到mm1
。 -
引用
mm2
并不是一個新的變量,而是mm1
的別名(另一個名字)。 -
此時,
mm2
和mm1
指向同一塊內存,修改mm2
就是修改mm1
,反之亦然。
-
-
int mm3 = 15;
-
定義一個整型變量
mm3
,并將其初始值設為15
。 -
內存中:
mm3
存儲的值是15
。
-
-
mm2 = mm3;
-
將
mm3
的值(15
)賦給mm2
。 -
由于
mm2
是mm1
的引用,這實際上是將15
賦給mm1
。 -
執行后:
-
mm1
的值變為15
。 -
mm2
的值也變為15
(因為它是mm1
的引用)。 -
mm3
的值仍然是15
(不受影響)。
-
-
最終結果:
-
mm1
的值:15
(被mm2 = mm3
修改) -
mm2
的值:15
(因為它是mm1
的引用) -
mm3
的值:15
(保持不變)
輸出:
mm1: 15
mm2: 15
mm3: 15
關鍵點總結:
-
引用(
&
)的本質:-
引用是變量的別名,不是獨立的變量。
-
int& mm2 = mm1;
表示mm2
是mm1
的另一個名字,兩者共享同一塊內存。
-
-
賦值操作的影響:
-
mm2 = mm3;
實際上是將mm3
的值賦給mm1
(因為mm2
是mm1
的引用)。 -
引用本身沒有獨立的內存空間,賦值操作會直接影響它綁定的變量。
-
-
與指針的區別:
-
如果是
int* p = &mm1; *p = mm3;
,效果和引用類似,但引用更簡潔且不能重新綁定。 -
引用一旦綁定后不能更改綁定的對象,而指針可以指向不同的對象。
-
引用本身一旦綁定后不能重新綁定到另一個對象,引用不能改變
關于 “引用不能改變” 的說法需要更精確的解釋。實際上,引用本身一旦綁定后不能重新綁定到另一個對象,但 引用綁定的對象的內容是可以改變的。這是兩個不同的概念:
1. 引用的“不可變性”(不能重新綁定)
-
引用一旦初始化后,不能指向其他變量:
-
引用在聲明時必須初始化,并且之后不能更改它綁定的對象。
-
例如:
-
int a = 10;
int b = 20;
int& ref = a; // ref 綁定到 a
ref = b; // 這是修改 ref 綁定的對象 a 的值,不是改變 ref 的綁定!// ref = &b; // 錯誤!不能重新綁定 ref 到 b(C++ 不允許)
-
關鍵點:
ref = b
是把b
的值賦給a
(因為ref
是a
的引用),而不是讓ref
指向b
。
2. 引用綁定的對象的內容可以改變
引用本身是別名,修改引用就是修改它綁定的對象
-
例如:
int a = 10;
int& ref = a; // ref 是 a 的別名
ref = 20; // 修改 ref 就是修改 a,a 現在是 20
常見誤區澄清
-
誤區:“引用不能改變” → 實際上是指 引用不能重新綁定,但可以修改它綁定的對象的內容。
正確說法:
-
引用 不能重新綁定(不能指向其他對象)。
-
引用 可以修改它綁定的對象的內容(因為它是別名)。
-
總結
-
引用一旦綁定后不能重新綁定(不能指向其他變量),這是它的“不可變性”。
-
引用綁定的對象的內容可以改變(通過引用直接修改)。
?mm2 = mm3
-
mm2
是mm1
的引用,所以mm2 = mm3
就是mm1 = mm3
。 -
修改的是
mm1
的值,mm2
仍然綁定到mm1
(不能重新綁定)。
二.引用的常屬性必須和目標的常屬性“一致”(個別情況也可以不一致)
const int e = 10;
int& f = e; // ×
const int& g = e; // √
可以限定更加嚴格(別名可以比真名更加嚴格)
int a = 10;const int& h = a; // OK
三.const 修飾一個常量(常引用)
在 C++ 中,const
引用(const T&
)是一種非常重要的特性,它結合了引用的效率和 const
的安全性。
1. 基本概念
-
const
引用:-
是一個對常量對象的引用,不能通過該引用修改所綁定的對象。
-
語法:
const T& ref = obj;
-
作用:提供對對象的只讀訪問,同時避免拷貝(與普通引用類似)。
與普通引用的區別:
-
特性 | 普通引用 (T& ) | const 引用 (const T& ) |
---|---|---|
能否修改對象 | 可以修改綁定的對象 | 不能修改綁定的對象 |
綁定對象類型 | 必須綁定可修改的對象 | 可以綁定 const 或非 const 對象 |
安全性 | 可能意外修改數據 | 提供只讀訪問,更安全 |
2. 核心知識點
(1) const
引用可以綁定到 const
和非 const
對象
-
可以綁定到非
const
對象(提供只讀訪問):
int x = 10;
const int& ref = x; // ref 是 x 的 const 引用
// ref = 20; // 錯誤!不能通過 ref 修改 x
-
可以綁定到
const
對象(合法且安全):
const int y = 20;
const int& ref = y; // ref 是 y 的 const 引用
// ref = 30; // 錯誤!y 本身就是 const
-
普通引用不能綁定到
const
對象:
const int z = 30;
int& ref = z; // 錯誤!普通引用不能綁定 const 對象
(2) const
引用可以延長臨時對象的生命周期
-
臨時對象(如函數返回值、表達式結果)通常只能綁定到
const
引用 -
用途:避免不必要的拷貝,同時保證安全性(不能修改臨時對象)
10; ? // 聲明周期很短,正常執行過了封號后 10這個內存地址就不在了int& ri = 10; ?// 報錯 ??? ?非常引用的初始值必須為左值,普通引用不能綁定臨時對象const int& ri = 10; ?// 正確,有了這個別名之后,聲明周期就變長了,會跟著別名的生命周期改變
了解一個左值和右值的概念:
具體名字的內存,能夠取地址 --》 左值 (非常左值[無const修復] | 常左值[有const修復])
匿名內存,不能取地址值 --》 右值 (認為直接更改右值沒有意義) 比如 10;
常引用可以作為任何東西的別名
特點:
1.引用可以延長右值的生命周期
2.常引用 即 萬能引用
3.引用的生命周期不能長于目標
int a = 10;int& ra = a; // OK 引用ra可以是a的別名
const int& ri = a; // OK 常引用 ri 可以是 a的別名const int b = 20;int& bb = b; // ERROR const int& bb = b; // OK 常引用bb可以是常量b的別名const int& rf = 10; // OK 常引用rf可以是常量10的別名int foo(){}const int& hg = foo(); // OK
(3) const
引用作為函數參數(推薦用法)
-
傳遞大對象時避免拷貝,同時防止函數內部修改參數:
void printValue(const std::string& str) {// str = "new value"; // 錯誤!不能修改std::cout << str << std::endl;
}int main() {std::string s = "Hello";printValue(s); // 傳遞 const 引用,避免拷貝
}
?
優勢:
高效(無拷貝)。
安全(函數不能意外修改參數)。
(4) const
引用與返回值
-
函數返回
const
引用:-
可以避免返回臨時對象的拷貝,同時防止調用者修改返回值:
-
const std::string& getString() {static std::string s = "Hello";return s; // 返回 static 變量的 const 引用
}
注意:如果返回局部變量的引用,會導致懸空引用(未定義行為)! ?
const std::string& getLocalString() {std::string s = "Hello";return s; // 錯誤!s 是局部變量,函數結束后被銷毀
}
(5) const
引用與重載
-
const
引用可以區分重載函數:
void func(const int& x) { std::cout << "const ref" << std::endl; }
void func(int& x) { std::cout << "non-const ref" << std::endl; }int main() {int a = 10;const int b = 20;func(a); // 調用非 const 版本func(b); // 調用 const 版本
}
?
-
編譯器會根據參數是否為
const
選擇正確的重載版本。
(6) const
引用與移動語義
-
const 引用不能用于移動語義:
-
移動語義需要修改源對象(“竊取”資源),而
const
引用禁止修改:
-
std::string s = "Hello";
const std::string& ref = s;
// std::string moved = std::move(ref); // 錯誤!ref 是 const
?正確做法:傳遞非 const
引用或值:
std::string moved = std::move(s); // 正確
3. 最佳實踐
-
優先使用
const
引用傳遞參數:-
避免拷貝,同時保證函數不會意外修改參數。
-
示例:
-
void process(const std::vector<int>& data) { ... }
-
返回
const
引用時注意生命周期:
-
只能返回全局/靜態對象的引用,不能返回局部變量的引用。
-
區分
const
和非const
重載:
-
提供更靈活的接口(如
std::vector
的operator[]
有const
和非const
版本)。
-
避免濫用
const
引用:
-
如果需要修改參數,使用普通引用。
-
如果需要返回可修改的對象,返回值或非
const
引用。
4. 常見誤區
-
誤區 1:“
const
引用不能綁定到臨時對象”-
糾正:
const
引用可以綁定到臨時對象(普通引用不能)。
-
-
誤區 2:“
const
引用可以修改綁定的對象”-
糾正:
const
引用不能修改綁定的對象(這是它的核心特性)。
-
-
誤區 3:“
const
引用總是比值傳遞好”-
糾正:如果對象很小(如
int
),值傳遞可能更高效(避免引用開銷)。
-
5. 總結
特性 | const 引用 (const T& ) |
---|---|
綁定對象 | 可以綁定 const 或非 const 對象 |
能否修改對象 | 不能(提供只讀訪問) |
臨時對象綁定 | 可以綁定臨時對象(普通引用不能) |
函數參數 | 推薦用于大對象,避免拷貝且保證安全性 |
返回值 | 可以返回 const 引用(但需注意生命周期) |
重載區分 | 可以與非 const 引用重載 |
移動語義 | 不能用于移動語義(因為不能修改源對象) |
核心原則:
-
const
引用 = 高效 + 安全(避免拷貝,防止意外修改)。 -
普通引用 = 高效 + 可修改(需要修改參數時使用)。
-
值傳遞 = 簡單 + 安全(小對象或需要獨立副本時使用)。
建議:合理使用 const
引用可以顯著提升代碼的性能和安全性!