九.C++ 對引用的學習

一.基本概念

引用即內存的別名

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

詳細執行過程:

  1. int mm1 = 20;

    • 定義一個整型變量 mm1,并將其初始值設為 20

    • 內存中:mm1 存儲的值是 20

  2. int& mm2 = mm1;

    • 定義一個整型引用 mm2,并將其綁定到 mm1

    • 引用 mm2 并不是一個新的變量,而是 mm1 的別名(另一個名字)。

    • 此時,mm2mm1 指向同一塊內存,修改 mm2 就是修改 mm1,反之亦然。

  3. int mm3 = 15;

    • 定義一個整型變量 mm3,并將其初始值設為 15

    • 內存中:mm3 存儲的值是 15

  4. mm2 = mm3;

    • mm3 的值(15)賦給 mm2

    • 由于 mm2mm1 的引用,這實際上是將 15 賦給 mm1

    • 執行后:

      • mm1 的值變為 15

      • mm2 的值也變為 15(因為它是 mm1 的引用)。

      • mm3 的值仍然是 15(不受影響)。

最終結果:

  • mm1 的值:15(被 mm2 = mm3 修改)

  • mm2 的值:15(因為它是 mm1 的引用)

  • mm3 的值:15(保持不變)

輸出

mm1: 15
mm2: 15
mm3: 15

關鍵點總結:

  1. 引用(&)的本質

    • 引用是變量的別名,不是獨立的變量。

    • int& mm2 = mm1; 表示 mm2mm1 的另一個名字,兩者共享同一塊內存。

  2. 賦值操作的影響

    • mm2 = mm3; 實際上是將 mm3 的值賦給 mm1(因為 mm2mm1 的引用)。

    • 引用本身沒有獨立的內存空間,賦值操作會直接影響它綁定的變量。

  3. 與指針的區別

    • 如果是 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(因為 refa 的引用),而不是讓 ref 指向 b

2. 引用綁定的對象的內容可以改變

引用本身是別名,修改引用就是修改它綁定的對象

  • 例如:

int a = 10;
int& ref = a;  // ref 是 a 的別名
ref = 20;      // 修改 ref 就是修改 a,a 現在是 20

常見誤區澄清

  • 誤區:“引用不能改變” → 實際上是指 引用不能重新綁定,但可以修改它綁定的對象的內容。

    正確說法:

    • 引用 不能重新綁定(不能指向其他對象)。

    • 引用 可以修改它綁定的對象的內容(因為它是別名)。

總結

  • 引用一旦綁定后不能重新綁定(不能指向其他變量),這是它的“不可變性”。

  • 引用綁定的對象的內容可以改變(通過引用直接修改)。

?mm2 = mm3
  • mm2mm1 的引用,所以 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. 最佳實踐
  1. 優先使用 const 引用傳遞參數

    • 避免拷貝,同時保證函數不會意外修改參數。

    • 示例:

void process(const std::vector<int>& data) { ... }
  1. 返回 const 引用時注意生命周期

  • 只能返回全局/靜態對象的引用,不能返回局部變量的引用。

  1. 區分 const 和非 const 重載

  • 提供更靈活的接口(如 std::vectoroperator[]const 和非 const 版本)。

  1. 避免濫用 const 引用

  • 如果需要修改參數,使用普通引用。

  • 如果需要返回可修改的對象,返回值或非 const 引用。

4. 常見誤區
  • 誤區 1:“const 引用不能綁定到臨時對象”

    • 糾正const 引用可以綁定到臨時對象(普通引用不能)。

  • 誤區 2:“const 引用可以修改綁定的對象”

    • 糾正const 引用不能修改綁定的對象(這是它的核心特性)。

  • 誤區 3:“const 引用總是比值傳遞好”

    • 糾正:如果對象很小(如 int),值傳遞可能更高效(避免引用開銷)。

5. 總結
特性const 引用 (const T&)
綁定對象可以綁定 const 或非 const 對象
能否修改對象不能(提供只讀訪問)
臨時對象綁定可以綁定臨時對象(普通引用不能)
函數參數推薦用于大對象,避免拷貝且保證安全性
返回值可以返回 const 引用(但需注意生命周期)
重載區分可以與非 const 引用重載
移動語義不能用于移動語義(因為不能修改源對象)

核心原則

  • const 引用 = 高效 + 安全(避免拷貝,防止意外修改)。

  • 普通引用 = 高效 + 可修改(需要修改參數時使用)。

  • 值傳遞 = 簡單 + 安全(小對象或需要獨立副本時使用)。

建議:合理使用 const 引用可以顯著提升代碼的性能和安全性!

    本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
    如若轉載,請注明出處:http://www.pswp.cn/news/908428.shtml
    繁體地址,請注明出處:http://hk.pswp.cn/news/908428.shtml
    英文地址,請注明出處:http://en.pswp.cn/news/908428.shtml

    如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

    相關文章

    7.2.1_順序查找

    知識總覽&#xff1a; 順序查找&#xff1a; 算法思想&#xff1a; 從頭到腳挨個找或者從腳到頭挨個找適用于線性表(順序存儲和鏈式存儲都適用)&#xff0c;又叫線性查找 實現&#xff1a; 1個數組elem指向數組的起始位置&#xff0c;索引從0開始遍歷數組直到找到目標值返回…

    視覺SLAM基礎補盲

    3D Gaussian Splatting for Real-Time Radiance Field Rendering SOTA方法3DGS contribution傳統重建基于點的渲染NeRF 基礎知識補盲光柵化SFM三角化極線幾何標準的雙目立體視覺立體匹配理論與方法立體匹配的基本流程李群和李代數 李群和李代數的映射李代數的求導李代數解決求導…

    如何利用 Redis 實現跨多個無狀態服務實例的會話共享?

    使用 Redis 實現跨多個無狀態服務實例的會話共享是一種非常常見且有效的方案。無狀態服務本身不存儲會話信息&#xff0c;而是將用戶的會話數據集中存儲在外部存儲中&#xff08;如 Redis&#xff09;&#xff0c;這樣任何一個服務實例都可以通過查詢外部存儲來獲取和更新用戶的…

    《chipyard》docker使用

    一、啟動/重啟服務 二、登入/退出 容器對象查看 sudo docker ps -a # 查看容器列表 登入已例化的容器 sudo docker exec -it -u root 737ed3ddd5ff bash # 737ed3ddd5ff<容器名稱/ID> 三、容器編輯 刪除單個容器 sudo docker stop <容器ID> #停止容器 s…

    瀏覽器工作原理06 [#]渲染流程(下):HTML、CSS和JavaScript是如何變成頁面的

    引用 瀏覽器工作原理與實踐 簡單回顧下上節前三個階段的主要內容&#xff1a;在HTML頁面內容被提交給渲染引擎之后&#xff0c;渲染引擎首先將HTML解析為瀏覽器可以理解的DOM&#xff1b;然后根據CSS樣式表&#xff0c;計算出DOM樹所有節點的樣式&#xff1b;接著又計算每個元素…

    AI書簽管理工具開發全記錄(十三):TUI基本框架搭建

    文章目錄 AI書簽管理工具開發全記錄&#xff08;十三&#xff09;&#xff1a;TUI基本框架搭建前言 &#x1f4dd;1.TUI介紹 &#x1f50d;2. 框架選擇 ??3. 功能梳理 &#x1f3af;4. 基礎框架搭建??4.1 安裝4.2 參數設計4.3 繪制ui4.3.1 設計結構體4.3.2 創建頭部4.3.3 創…

    CC7利用鏈深度解析

    CommonsCollections7&#xff08;CC7&#xff09;是CC反序列化利用鏈中的重要成員&#xff0c;由Matthias Kaiser在2016年發現。本文將從底層原理到實戰利用&#xff0c;全面剖析這條獨特而強大的利用鏈。 一、CC7鏈技術定位 1.1 核心價值 無第三方依賴&#xff1a;僅需JDK原…

    openvino使用教程

    OpenVINO使用教程 本專欄內容支持平臺章節計劃 本專欄內容 OpenVINO 是一款開源工具包&#xff0c;用于在云端、本地和邊緣部署高性能 AI 解決方案。我們可以使用來自最熱門模型框架的生成式和傳統 AI 模型來開發應用程序。充分利用英特爾 硬件的潛力&#xff0c;使用openvino…

    ESP8266(NodeMcu)+GPS模塊+TFT屏幕實現GPS碼表

    前言 去年寫過一篇關于使用esp8266(nodemcu)gps模塊oled屏幕diy的gps定位器的文章.點擊回顧 .無奈OLED屏幕太小了,最近剛好有時間又折騰使用TFT屏幕diy了一款gps碼表 效果如圖 材料準備 依舊是請出我們的兩位老演員 nocdmcu一塊. GPS定位模塊(我買的大夏龍雀的DX-GP10-GP…

    解決獲取視頻第一幀黑屏問題

    文章目錄 解決獲取視頻第一幀黑屏問題核心代碼 解決獲取視頻第一幀黑屏問題 廢話不多說&#xff0c;直接上代碼&#xff1a; <script setup> const status ref(請點擊“添加視頻”按鈕添加視頻) const videoElement ref(document.createElement(video)) const curren…

    通過BUG(prvIdleTask、pxTasksWaitingTerminatio不斷跳轉問題)了解空閑函數(prvIdleTask)和TCB

    一、前言與問題 在基于 FreeRTOS 的嵌入式系統中&#xff0c;我使用 STM32F1 開發一個 MQTT 客戶端應用&#xff0c;涉及兩個主要任務&#xff1a; ATRecvParser&#xff1a;負責解析 Wi-Fi 模塊的 AT 命令響應&#xff08;如 OK、ERROR 和 IPD 數據&#xff09;。MQTT_Clien…

    繼MySQL之后的技術-JDBC-從淺到深-02

    目錄 概念 編程六部曲 SQL注入和statement 工具類的封裝 JDBC事務 模糊查詢 批處理 數據庫連接池 Apache-DBUtils BasicDao 概念 JDBC為訪問不同的數據庫提供了統一的接口&#xff0c;為使用者屏蔽了細節問題。 Java程序員使用JDBC&#xff0c;可以連接任何提供了JD…

    【配置 YOLOX 用于按目錄分類的圖片數據集】

    現在的圖標點選越來越多&#xff0c;如何一步解決&#xff0c;采用 YOLOX 目標檢測模式則可以輕松解決 要在 YOLOX 中使用按目錄分類的圖片數據集&#xff08;每個目錄代表一個類別&#xff0c;目錄下是該類別的所有圖片&#xff09;&#xff0c;你需要進行以下配置步驟&#x…

    淺談python如何做接口自動化

    工具與環境準備 開發工具 PyCharm專業版&#xff1a;支持項目視圖、代碼導航、調試功能和主流框架開發官方資源&#xff1a;JetBrains PyCharm 數據庫操作 使用mysqlclient庫操作MySQL&#xff08;Django官方推薦&#xff09;安裝命令&#xff1a;pip install mysqlclient1.3.…

    知識圖譜技術概述

    一、概述 知識圖譜&#xff08;Knowledge Graph&#xff09; 是一種基于圖結構的語義網絡&#xff0c;用于表示實體及其之間的關系&#xff0c;旨在實現更智能的知識表示和推理。它通過將現實世界中的各類信息抽象為 “實體-關系-實體” 的三元組結構&#xff0c;構建出復雜的知…

    NodeJS Koa 后端用戶會話管理,JWT, Session,長短Token,本文一次性講明白

    前言 前幾天&#xff0c;我寫了一篇文章&#xff0c;《我設計的一個安全的 web 系統用戶密碼管理流程》。其中著重點是講的如何利用非對稱加密進行安全的設計&#xff0c;并在講述了原理之后&#xff0c;又寫了 《node 后端和瀏覽器前端&#xff0c;有關 RSA 非對稱加密的完整…

    0.5S 級精度背后:DJSF1352-RN-6 如何讓儲能電站的每 1kWh 都「有跡可循」?

    1、背景 在能源轉型的時代洪流里&#xff0c;大型儲能電站作為保障電網穩定運行、平衡能源供需的核心基礎設施&#xff0c;其戰略價值愈發凸顯。而儲能電站的高效運轉&#xff0c;始終離不開精準的電能計量體系支撐。今日為您重點推介一款針對 1500V 儲能系統研發的專業電能表…

    Linux運維筆記:服務器安全加固

    文章目錄 背景加固措施1. 修改用戶密碼2. 使用公鑰認證替代密碼登錄3. 強化系統安全4. 掃描與清理殘留威脅5. 規范軟件管理&#xff08;重點&#xff09; 注意事項總結 提示&#xff1a;本文總結了大學實驗室 Linux 電腦感染挖礦病毒后的安全加固措施&#xff0c;重點介紹用戶密…

    Pycharm 配置解釋器

    今天更新了一版pycharm&#xff0c;因為很久沒有配置解釋器了&#xff0c;發現一直失敗。經過來回試了幾次終于成功了&#xff0c;記錄一下過程。 Step 1 Step 2 這里第二步一定要注意類型要選擇python 而不是conda。 雖然我的解釋器是conda 里面建立的一個環境。挺有意思的

    【Linux】awk 命令詳解及使用示例:結構化文本數據處理工具

    【Linux】awk 命令詳解及使用示例&#xff1a;結構化文本數據處理工具 引言 awk 是一種強大的文本處理工具和編程語言&#xff0c;專為處理結構化文本數據而設計。它的名稱來源于其三位創始人的姓氏首字母&#xff1a;Alfred Aho、Peter Weinberger 和 Brian Kernighan。 基…