《C++進階:引用補充、內聯函數與nullptr 核心用法》

😘個人主頁:@Cx330?

👀個人簡介:一個正在努力奮斗逆天改命的二本覺悟生

📖個人專欄:《C語言》《LeetCode刷題集》《數據結構-初階》《C++知識分享》

🌟人生格言:心向往之行必能至

前言:上篇博客中,我們掌握了引用的小部分,這篇博客會接著把引用剩余的部分講解給大家,然后還會給大家認識內聯函數與nullptr的核心用法,掌握號這些知識,我們就可以進入類和對象的學習中去了


目錄

一.引用(補充)

const引用:

關鍵點:

舉例說明:

圖示如下:

指針和引用的關系:(面試考點)

二.inline內聯函數

關鍵點:

舉例說明:

三.nullptr

關鍵點:

舉例說明:


一.引用(補充)

const引用:

關鍵點:
  • 可以引用?個const對象,但是必須用const引用。const引用也可以引用普通對象,因為對象的訪問權限在引用過程中可以縮小,但是不能放大。
  • 需要注意的是,在一些場景下,比如類型轉換中會產生臨時對象存儲中間值,也就是說我下面的rb和rp引用的都是臨時對象,而C++規定臨時對象具有常性,所以這里就觸發了權限放大,必須要使用常引用才可以
  • 所謂臨時對象就是編譯器需要?個空間暫存表達式的求值結果時臨時創建的?個未命名的對象,C++中把這個未命名對象叫做臨時對象。
舉例說明:

1.引用和指針的權限放大和縮小問題(放大不行,縮小可以):

#include<iostream>
using namespace std;
int main()
{const int a = 0;//權限放大(不能)//int& b = a;int c = 0;//權限縮小(能)const int& d = c;return 0;
}
//權限的放大和縮小,只存在于const指針和const引用
//我們再來看看指針#include<iostream>
using namespace std;int main()
{const int a = 0;const int* p1 = &a;//int& p2 = p1;//這個也屬于權限的放大,得寫成下面這樣const int* p2 = p1;//但是權限縮小還是可以的int c = 0;int* p3 = &c;const int* p4 = p3;return 0;
}

2.const可以引用常量,作為函數參數時如果不是為了讓形參的改變可以影響實參,是可以const修飾引用的,這樣傳參的時候選擇更多 :

#include<iostream>
using namespace std;int main()
{int i = 0;double d = i;//這個是可以通過編譯的,涉及隱式類型轉換,因為int和double本質上都是關于數據類型大小的。//像整型和指針就只能用強制類型轉換,如下int p = (int)&i;//但是我們再來看看引用里面的使用int j = 1;//double& rd = j;//不行const double& rd = j;//這個就可以了//為什么呢?--我們先不急再看一個例子//int& rp = (int)&j;//不行const int& rp = (int)&j;//可以//-------------------------具體原因分析(配合圖片)------------------------------------//這是因為在引用里面,轉換的過程中會產生一個臨時對象保存中間值。//所以實際上rb,rp引用的都是中間值,在C++里這個臨時對象是具有常性的(即被const修飾)//因此我們這里如果直接轉換的話,就會出現權限放大的錯誤,我們必須使用常引用(即const修飾)return 0;
}
圖示如下:

指針和引用的關系:(面試考點)

C++中指針和引用就像兩個性格迥異的親兄弟,指針是哥哥,引用是弟弟,在實踐中他們相輔相成,相得益彰。功能有重疊性,但是也有各自的特點,互相不可替代:

  • 語法概念上引用是一個變量的取別名不開空間,指針是存儲?個變量地址,要開空間。(我們一般盡量取談語法層,底層只在一些特殊場景下用來輔助了解)
  • 引用在定義時必須初始化,指針建議初始化,但是語法上不是必須的。
  • 引用在初始化時引用?個對象后,就不能再引用其他對象;而指針可以在不斷地改變指向對象。
  • 指針很容易出現空指針和野指針的問題,引用很少出現,引用使用起來相對更安全?些。
  • 引用可以直接訪問指向對象,指針需要解引用才是訪問指向對象。
  • sizeof中含義不同,引用結果為引用類型的大小,但指針始終是地址空間所占字節個數(32位平臺下占4個字節,64位下是8byte)

二.inline內聯函數

關鍵點:

  • 用inline修飾的函數叫做內聯函數,編譯時C++編譯器會在調用的地方展開內聯函數,這樣調用內聯函數就不需要建立棧幀了,可以提高效率。
  • inline對于編譯器而言只是一個建議,也就是說,你加了inline編譯器也可以選擇在調用的地方不展開,不同編譯器關于inline什么情況展開各不相同,因為C++標準沒有規定這個。inline適用于頻繁調用的短小函數,對于遞歸函數,代碼相對多?些的函數,加上inline也會被編譯器忽略。

  • C語言實現宏函數也會在預處理時替換展開,但是宏函數實現很復雜很容易出錯的,且不方便調試,C++設計了inline目的就是替代C的宏函數。
  • vs編譯器 debug版本下面默認是不展開inline的,這樣方便調試,debug版本想展開需要設置?下兩個地方(后面的舉例說明中會有)。
  • inline不建議聲明和定義分離到兩個文件,分離會導致鏈接錯誤。因為inline被展開,就沒有函數地址,鏈接時會出現報錯。

舉例說明:

先來看看之前C語言中宏函數里面的一些坑吧,以ADD函數為例:

錯誤一:

#define ADD(int x,int y) return x+y;

這個錯誤很離譜,我們要牢記宏是一種替換機制,這里直接寫成了一個函數,很明顯是錯誤的

錯誤二:

//宏是一種替換機制
//#define ADD(int x,int y) return x+y;
//錯誤寫法二:
//#define ADD(a,b) a+b;
//宏定義不要帶分號
//我們把分號去掉,但是還是有問題的
#define ADD(a,b) a+busing namespace std;int main()
{int ret1 = ADD(1, 2);//展開之后:int ret1 = 1 + 2;;,會出現兩個分號,這里還不會報錯,我們再來看看下面的//int ret2 = ADD(1, 2) * 3;//這里就出問題了//我們就算不帶分號,上面這個ret2最后的值也是錯的int ret2 = ADD(1, 2) * 3;//我們想要得到的是9,但是我們打印出來是7cout << ret2 << endl;//因為展開之后:1 + 2 * 3 = 7//這里的優先級被影響了return 0;
}

宏定義時,不要加分號,還需要加上()來保持優先級

錯誤三:

#define ADD(a,b) (a+b)
#include<iostream>
using namespace std;int main()
{//這樣寫ret2打印出來的結果是我們想要的9int ret2 = ADD(1, 2) * 3;cout << ret2 << endl;//但是這種寫法還是存在一些問題的int x = 0, y = 1;ADD(x | y, x & y);//展開會變成:(x | y + x & y)//+號的優先級高于 |和& 所以這里相當于(x|(y+x)&y)return 0;
}

帶上了外面的括號,ret2的問題解決了。但是在一些場景下還是有問題

正確寫法:

//正確寫法:
#define ADD(a,b) ( (a) + (b) )
#include<iostream>
using namespace std;int main()
{//這樣寫ret2打印出來的結果是我們想要的9int ret2 = ADD(1, 2) * 3;cout << ret2 << '\n';//這種寫法也沒問題了int x = 0, y = 1;ADD(x | y, x & y);//展開會變成:( (x | y) + (x & y) ),符合我們的目的return 0;
}

💡Tips:

宏函數這么復雜,容易寫出問題,還不能調試。

那我們為什么還要用它呢,它的優勢在于什么呢?
優點:高頻調用小函數時,寫成宏函數,可以提高效率,預處理階段宏會替換,提高效率,不建立棧幀

我們在C++中使用inline內聯函數代替宏函數該怎么寫:

inline int ADD(int x, int y)
{return x + y;
}

和函數的寫法差不多,但是是不一樣的。它編譯是直接展開的跟宏一樣,不會創建棧幀空間?

因為默認debug版本下,為了方便調試,inline也不展開。我們需要完成兩設置:

代碼:

#include<iostream>
using namespace std;//轉反匯編看,發現還是有call還是創建了棧幀,這是為什么
inline int ADD(int a, int b)
{return a + b;
}
//因為默認debug版本下,為了方便調試,inline也不展開。
//我們需要設置一下--這里大家可以自己測試看看,最號=好用低版本的vsint main()
{int ret2 = ADD(1, 2) * 3;cout << ret2 << '\n';//打印出來也是9,完全沒有問題return 0;
}

設置步驟:

  1. 右鍵單擊解決方案資源管理器中的項目,選擇“屬性”。
  2. 在彈出的屬性對話框中,找到“C/C++”選項卡,點擊“常規”。
  3. 在“調試信息格式”下拉菜單中,選擇“程序數據庫(/Zi)”。
  4. 接著點擊“C/C++”下的“優化”選項。
  5. 在“內聯函數的擴展”下拉菜單中,選擇“只適用于_inline(/Ob1)”。

inline只是一個建議,展開還是創建空間由編譯器說的算,遞歸和代碼多的函數可能就不會展開:

#include<iostream>
using namespace std;inline int ADD(int a, int b)
{a *= 2;a *= 2;a *= 2;a *= 2;a *= 2;a *= 2;a *= 2;a *= 2;a *= 2;a *= 2;//5個的時候還是可以展開的,10個就不行了return a + b;
}int main()
{int ret2 = ADD(1, 2) * 3;cout << ret2 << '\n';return 0;
}

圖示如下:

思考:為什么只是建議呢?

如果完全交給程序員,可能會出現代碼指令惡性膨脹的問題,導致可執行程序(安裝包)過大,這是特別不好的。所以編譯器會自己把握這個展開還是不展開,有其自己的邏輯和判斷。

inline不建議聲明和定義放離到兩個文件,分離會導致鏈接錯誤。因為inline被展開,就沒有函數地址,鏈接時會出錯:(注意看注釋)

拿順序表為例,我直接給正確改法了,然后它的.cpp文件和.h文件這里是截圖的

SeqList.h:(內聯函數直接在.h文件中實現就可以了)

SeqList.cpp:(在.cpp文件中不需要再實現內聯函數了,可以看出我這里注釋掉了)

test.cpp:

#include"SeqList.h"int main()
{SL s;//我實現用的引用所以不用傳地址SLInit(s); // call 地址return 0;
}

三.nullptr

NULL實際是?個宏,在傳統的C頭文件(stddef.h)中,可以看到如下代碼:

關鍵點:

  • C++中NULL可能被定義為字面常量0,或者C中被定義為無類型指針(void*)的常量。不論采取何種定義,在使用空值的指針時,都不可避免的會遇到?些麻煩,本想通過f(NULL)調?指針版本的f(int*)函數,但是由于NULL被定義成0,調用了f(int x),因此與程序的初衷相悖。f((void*)NULL);調用會報錯。
  • C++11中引入nullptr,nullptr是?個特殊的關鍵字,nullptr是?種特殊類型的字面量,它可以轉換成任意其他類型的指針類型。使用nullptr定義空指針可以避免類型轉換的問題,因為nullptr只能被隱式地轉換為指針類型,而不能被轉換為整數類型。

舉例說明:

#include<iostream>
using namespace std;void f(int x)
{cout << "f(int x)" << endl;
}void f(int* ptr)
{cout << "f(int* ptr)" << endl;
}int main()
{f(0);f(NULL);//f((void*)0);--有個圖片//用上面的都會執行出來函數1,而不會是函數2f(nullptr);//但是用nullptr就很清晰了,可以很好處理這個問題int* p1 = NULL;char* p2 = NULL;//以后我們在C++里面置為空都這樣寫int* p3 = nullptr;char* p4 = nullptr;return 0;
}

這里可以看出用NULL時并沒有達到我想要的效果,但是用nullptr可以?

完整源代碼:

CPP專屬倉庫: 【CPP知識學習倉庫】 - Gitee.com


往期回顧:

《C++起源與核心:版本演進+命名空間法》

《C++基礎:輸入輸出、缺省參數,函數重載與引用的巧妙》

總結:這篇博客到這里就結束了,我們C++人們知識也就告一段落,接下來就會進入我們類和對象的學習中去,如果文章對你有幫助的話,歡迎評論,點贊,收藏加關注,感謝大家的支持。

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

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

    相關文章

    通過python程序將實時監測數據寫入excel軟件進行保存是常用和非常實用的功能,本文教會大家怎么去搞定此功能

    目錄 一、功能介紹 二、具體的程序示例 三、實際應用建議 一、功能介紹 本方案的核心功能是持續監聽一個數據源&#xff08;如傳感器、API接口、消息隊列、其他應用程序等&#xff09;&#xff0c;將獲取到的實時數據流以追加的方式寫入到Excel文件中。同時&#xff0c;方案…

    在 Linux 中全局搜索 Word 文檔內容的完整指南

    文章目錄 為什么不能直接使用 grep 搜索 Word 文檔? 解決方案:使用 Pandoc 轉換后搜索 步驟 1:安裝 Pandoc 步驟 2:創建搜索腳本 步驟 3:執行搜索(兩者選其一) 一行命令解決方案 高級用法與優化 1. 忽略大小寫搜索 2. 顯示匹配內容 3. 性能優化 注意事項 結論 在日常工作中…

    基于STM32單片機智能農業大棚控制系統-插件款 DIY 設計開源(實物+程序+原理圖+其他資料)

    目錄 一、項目成品展示 二、功能介紹 三、硬件組成 四、PCB展示 五、程序設計 六、資料分享 資料獲取 查看主頁介紹&#xff1a;兆龍電子單片機設計 一、項目成品展示 項目成品圖片展示&#xff1a; 嗶哩嗶哩視頻鏈接&#xff1a; STM32單片機智能農業大棚控制系統-插件…

    如何實現二維CAD與3D建模工程圖關聯一體化出圖 | 中望3D 2026新亮點

    本文為CAD芯智庫整理&#xff0c;未經允許請勿復制、轉載&#xff01;原文轉自&#xff1a;www.xwzsoft.com/h-nd-609.htmlwww.xwzsoft.com/h-nd-609.html許多企業在同時使用二三維CAD軟件時&#xff0c;往往因為2D和3D是不同軟件商開發&#xff0c;很容易遇到問題&#xff1a;…

    深入理解 Roo Code 的自動批準功能

    在軟件開發過程中&#xff0c;效率與安全往往是兩個需要不斷平衡的主題。 Roo Code 中一項能夠顯著提升效率但也需要謹慎使用的功能——自動批準&#xff08;Auto-Approval&#xff09;。如果你經常與 AI 助手協作編碼&#xff0c;這個功能可能會改變你的工作流&#xff0c;但錯…

    《一次高并發場景下疑難Bug的深度排查與復盤》

    常規Bug如同路上的小石子,彎腰便可清理;但有些隱藏在架構深處、僅在特定場景下爆發的疑難Bug,卻像深淵中的暗礁,不僅會讓程序驟然停擺,更可能消耗團隊數周甚至數月的精力。我曾親歷過這樣一場“戰役”—一個僅在高并發峰值時段出現、無規律觸發系統崩潰的Bug,從最初的毫無…

    互聯網大廠Java面試實錄:Spring Boot與微服務架構解析

    第一輪&#xff1a;基礎技術棧 面試官: 小C&#xff0c;你能否簡要介紹一下Java SE 8中的Lambda表達式&#xff1f; 小C: Lambda表達式就是Java中的匿名函數&#xff0c;可以簡化代碼&#xff0c;讓代碼更優雅。我記得它可以用來替代匿名類&#xff0c;特別是在集合操作中很有用…

    滲透測試報告編寫平臺 | 簡化和自動化滲透測試報告的生成過程。

    工具介紹 這是一個基于 FastAPI 和 Vue.js 的 Web 應用程序&#xff0c;旨在簡化和自動化安全測試報告的生成過程。 <AI編寫 能用就行> 主要功能 模板管理: 上傳和管理 .docx 格式的報告模板。報告生命周期管理: 創建、編輯、查看和刪除安全測試報告。漏洞知識庫: 管理和…

    Vulkan 學習路線圖

    按階段拆解&#xff0c;告訴你每個階段要寫哪些 Demo&#xff0c;逐步從三角形走到完整渲染器。&#x1f539; 第一階段&#xff1a;入門&#xff08;Hello Vulkan&#xff09;目標&#xff1a;跑通 Vulkan 的最小化程序&#xff0c;理解基本對象。 要做的 Demo&#xff1a;創建…

    C語言指針5

    文章目錄1.sizeof和strlen對比1.1sizeof1.2strlen1.3sizeof和strlen的對比2.數組和指針的筆試題2.1一維數組2.2字符數組2.3二維數組3.指針運算筆試題1.sizeof和strlen對比 1.1sizeof 在學習操作符的時候&#xff0c;我們學習了 sizeof。sizeof 用于計算變量所占內存空間的大小…

    【二叉樹 - LeetCode】617. 合并二叉樹

    題目&#xff1a; 617. 合并二叉樹 - 力扣&#xff08;LeetCode&#xff09; 題解&#xff1a; /*** Definition for a binary tree node.* struct TreeNode {* int val;* TreeNode *left;* TreeNode *right;* TreeNode() : val(0), left(nullptr), right(n…

    AI + 醫療:除了影像診斷,智能技術還在改寫哪些診療環節?

    一、引言在科技飛速發展的當下&#xff0c;人工智能&#xff08;AI&#xff09;已成為醫療領域變革的重要驅動力。提及 AI 在醫療中的應用&#xff0c;大眾首先想到的往往是醫學影像診斷&#xff0c;AI 的確在該領域成果斐然&#xff0c;如快速識別肺結節、精準分析影像細節&am…

    立軸式小型混凝土攪拌機的設計含14張CAD

    摘要 目前&#xff0c;混凝土攪拌機在國內外都有著飛速的發展&#xff0c;國際競爭力在不斷提高。 為了滿足市場需求&#xff0c;完善產品系列&#xff0c;適應小型建筑施工和實驗室工作的需求&#xff0c;設 計了此混凝土攪拌機。 本課題主要研究立軸式混凝土攪拌機的工作原理…

    深度剖析Spring AI源碼(七):化繁為簡,Spring Boot自動配置的實現之秘

    深度剖析Spring AI源碼&#xff08;七&#xff09;&#xff1a;化繁為簡&#xff0c;Spring Boot自動配置的實現之秘“Any sufficiently advanced technology is indistinguishable from magic.” —— Arthur C. Clarke Spring Boot的自動配置就是這樣的"魔法"。只需…

    PNP機器人介紹:全球知名具身智能/AI機器人實驗室介紹之多倫多大學機器人研究所

    PNP機器人介紹&#xff1a;全球知名具身智能/AI機器人實驗室介紹之多倫多大學機器人研究所1&#xff0c;多倫多大學機器人研究所介紹多倫多大學機器人研究所&#xff08;University of Toronto Robotics Institute, 簡稱UTRI&#xff09;是加拿大規模最大、跨學科最多樣化的機器…

    計算機網絡-1——第一階段

    文章目錄一、網絡結構體系1.1 OSI七層模型&#xff08;理論標準&#xff09;2. TCP/IP 四層模型&#xff08;實際應用&#xff09;二、計算機網絡設備三、網絡的分類及IP地址介紹3.1 網絡分類3.2 IP地址介紹四、常見協議4.1 TCP協議與UDP協議4.1.1 TCP協議4.1.2 UDP協議4.1.3 T…

    數據結構青銅到王者第三話---ArrayList與順序表(2)

    續接上一話&#xff1a; 目錄 一、ArrayList的使用&#xff08;續&#xff09; 1、ArrayList的擴容機制&#xff08;續&#xff09; 五、ArrayList的相關練習 1、楊輝三角 2、簡單的洗牌算法 六、ArrayList的問題及思考 一、ArrayList的使用&#xff08;續&#xff09; …

    [Vid-LLM] docs | 視頻理解任務

    鏈接&#xff1a;https://github.com/yunlong10/Awesome-LLMs-for-Video-Understanding docs&#xff1a;Vid-LLM 本項目是關于視頻大語言模型(Vid-LLMs)的全面綜述與精選列表。 探討了這些智能系統如何處理和理解視頻內容&#xff0c;詳細介紹了它們多樣的架構與訓練方法、旨…

    構建高可用Agent狀態管理API:Gin+GORM全流程解析

    繼寫給 Javaer 看的 Go Gin 教程 之后新寫一篇真實的go開發教程:技術棧?&#xff1a;Go 1.21 Gin 1.9 GORM 2.0 MySQL 5.7 Docker一、技術選型&#xff1a;為什么是GinGORM&#xff1f;1.?性能與簡潔性平衡???Gin?&#xff1a;基于httprouter的高性能框架&#xff0c…

    [Java惡補day51] 46. 全排列

    給定一個不含重復數字的數組 nums &#xff0c;返回其 所有可能的全排列 。你可以 按任意順序 返回答案。 示例 1&#xff1a; 輸入&#xff1a;nums [1,2,3] 輸出&#xff1a;[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]] 示例 2&#xff1a; 輸入&#xff1a;nums …