C++引用、內(nèi)聯(lián)函數(shù)與nullptr超全解析(新手避坑指南)
前言:
繼命名空間、缺省參數(shù)等基礎(chǔ)內(nèi)容之后,本篇主要講述三個核心特性:引用、內(nèi)聯(lián)函數(shù)與 nullptr。它們是簡化代碼結(jié)構(gòu)、優(yōu)化程序性能的重要手段,也是不能輕易理解的知識點,尤其是引用。如何正確使用引用避免權(quán)限問題?內(nèi)聯(lián)函數(shù)的適用場景是什么?為何推薦用 nullptr 替代 NULL?帶著這些疑問,本文徹徹底底幫你弄清楚這些概念。
一、 引用
1. 引用基礎(chǔ)
概念:給已有的變量“新名字”(別名)
使用:類型&引?別名=引?對象;
案例:在需要傳指針的地方,可以用引用代替,不需要調(diào)用該指針,讓形參就叫別名,改變形參就是改變實參
特性:
- 引用在定義的時候必須初始化
- 一個變量可以有多個引用
- 引用一旦引用一個實體,再不能引用其他實體
int a = 10; int& ra; // 編譯報錯:“ra”: 必須初始化引? int& b = a; int c = 20; // 這?并?讓b引?c,因為C++引?不能改變指向, // 這?是?個賦值
主要應(yīng)用:
- 函數(shù)參數(shù)傳遞
- 函數(shù)返回值
引用返回值能不能隨便用?
2. 引用返回注意事項:
int& func()
{
int a = 0;
return a;//錯的:a是局部的,返回時候已經(jīng)銷毀,類似野指針
}
int& func1(int& ra)
{
ra = 3
return ra;//對的:ra是外部傳入的引用,出函數(shù)依舊存在
}
3.const引用規(guī)則:
權(quán)限可縮小不可放大,僅限制引用本身的訪問權(quán)限
int main()
{
const int a = 0;
int& ra = a;//錯的
const int& ra = a;//對的
//權(quán)限放大
//注意分辨:
const int a = 0;
int ra = a;//這是可以的,不是權(quán)限放大
int b = 0;
const int& rb = b;//對的
//權(quán)限縮小
//這個地方?jīng)]有縮小b的權(quán)限,b依舊能該改變
b++;//對
rb++;//錯
//類比:公共場合,添加一個身份,做不了一些事情(權(quán)限縮?。?
int& rc = 30;//錯
const int& rc = 30;//對
//const引用可以給常量取別名,單純的引用不可以
//編譯器需要?個空間暫存表達式的求值結(jié)果(中間值)時臨時創(chuàng)建的?個未命名的對象
int rd = (a+b);//可以對臨時對象賦值
int& rd = (a+b);//不可以對臨時對象單純引用(沒法修改)
const int& rd = (a+b);//可以用const引用(不修改)
double d = 12.34;
int i = d;//是隱式類型轉(zhuǎn)換,這個過程有臨時對象
int& i = d;//不可以
const int& i = d;//可以
//下面介紹一個很好用的東西
//函數(shù)模板,T可以是任意類型
template <class T>
void func(T val)
{
//注意看這里傳入的T是任意類型
//所以程序員對它進行一個引用(&T val)(防止傳入的值太大拷貝成本高)
//這里傳常量/臨時對象/帶有類型轉(zhuǎn)換可以嗎?不行
//最終:const &T val,這樣子就什么都能傳
// 使用const引用可接收:
// 1. 普通變量
// 2. 常量
// 3. 臨時對象
// 4. 類型轉(zhuǎn)換結(jié)果
// 類似void*的通用性
}
return 0;
}
4. 指針和引用的關(guān)系與區(qū)別
- 引用不額外開空間,指針是開一個空間來存儲地址
- 引用使用必須初始化,指針不是必須要求的
- 引用只能初始化一次,不能更改,指針可以更改指向?qū)ο?/li>
- 引用可以直接訪問對象(別名),指針需要解引用。
- sizeof()引用返回的是引用類型的大小,而指針則根據(jù)操作系統(tǒng)不一樣大小不一樣
- 指針有野指針和空指針的問題,引用很少有
int* ptr = nullptr; int& rptr = *ptr; rptr++;

二、inline 和 宏函數(shù)
1. 宏函數(shù)
- 在C語言里有通過
#define定義的宏函數(shù),為了防止,宏函數(shù)的使用往往小心翼翼,要加很多層括號 - 本質(zhì):預(yù)編譯階段的文本替換,不是真正的函數(shù)調(diào)用。
- 目的:在避免函數(shù)調(diào)用棧幀開銷的同時,實現(xiàn)代碼復(fù)用(比普通函數(shù)更輕量,比內(nèi)聯(lián)函數(shù)更「強制」替換)。
- 展示:
//正確
#define ADD(a,b) ((a)+(b))
int main()
{
int add = ADD(3,2);
return 0;
}
//錯誤加分號
#define ADD(a,b) ((a)+(b));
int main()
{
if (ADD(3, 2) > 0)
{
//執(zhí)行某操作
}
return 0;
}
#define ADD(a,b) (a+b)
//錯誤不加內(nèi)層括號
int main()
{
int add = ADD(1 ^ 1, 2 ^ 2);
//替換以后 =》1^(1+2)^2
return;
}
#define ADD(a,b) (a)+(b);
//錯誤外層無括號
int main()
{
int add = ADD(3,2) * ADD(3,2);
//替換以后 =》3+(2*3)+2
return 0;
}
以上是宏函數(shù)的使用
2. inline(內(nèi)聯(lián)函數(shù))
上面宏函數(shù)都二條提到了內(nèi)聯(lián)函數(shù),inline是一個關(guān)鍵字,他所修飾的函數(shù)稱之為內(nèi)聯(lián)函數(shù),上文提到宏函數(shù)是為減少函數(shù)棧幀的開銷,inline也有這個作用
- 內(nèi)聯(lián)函數(shù)有什么用?
內(nèi)聯(lián)函數(shù)是一個類似”宏函數(shù)“的 函數(shù) ,不同于宏函數(shù),內(nèi)聯(lián)函數(shù)是在函數(shù)內(nèi)部計算以后返回值,有的朋友要說:函數(shù)不都這樣嗎?其實不然,內(nèi)聯(lián)函數(shù)的本質(zhì)是把函數(shù)的獨立棧幀省略掉,直接使用調(diào)用者的棧幀。
這里插入棧幀的概念:
棧幀(也叫「活動記錄」)是程序運行時,棧內(nèi)存中為單個函數(shù)調(diào)用分配的一塊獨立內(nèi)存區(qū)域,用于存儲該函數(shù)的:
- 局部變量
- 函數(shù)參數(shù)
- 返回地址(函數(shù)執(zhí)行完后要回到調(diào)用者的哪一行代碼)
- ?;罚‥BP/RBP 寄存器,用于定位棧幀內(nèi)的變量)
- 臨時數(shù)據(jù)(比如表達式計算的中間結(jié)果)
- 程序的調(diào)用棧(Call Stack)就是由多個嵌套的棧幀組成的,比如
main() 調(diào)用funcA(),funcA() 又調(diào)用funcB(),棧中會依次壓入:main棧幀 →funcA棧幀 →funcB棧幀(棧頂)。
比如說:在main函數(shù)中調(diào)用一個inline int add()的函數(shù),如果去掉inline,他會創(chuàng)建棧幀;而加上inline變?yōu)閮?nèi)聯(lián)函數(shù)以后就在main函數(shù)的棧幀上創(chuàng)建自己的棧幀,省去了跳轉(zhuǎn)等操作,簡單了許多
當(dāng)函數(shù) A 調(diào)用函數(shù) B 時:
- 函數(shù) A 是「調(diào)用者(Caller)」,它的棧幀就是「調(diào)用者的棧幀」;
- 函數(shù) B 是「被調(diào)用者(Callee)」,它的棧幀就是「被調(diào)用者的棧幀」。
「調(diào)用者的棧幀」就是發(fā)起調(diào)用的那個函數(shù)在棧上的內(nèi)存區(qū)域,在函數(shù)調(diào)用過程中,它會暫時被「被調(diào)用者的棧幀」覆蓋(棧頂偏移),但調(diào)用結(jié)束后,CPU 會回到調(diào)用者的棧幀繼續(xù)執(zhí)行。
那內(nèi)聯(lián)函數(shù)好處這么多,能不能所有函數(shù)都直接前面加inline使其變成內(nèi)聯(lián)函數(shù)呢?
答案是 不可以 ,因為內(nèi)聯(lián)函數(shù)還是在調(diào)用者的內(nèi)部把函數(shù)展開了,有點類似與你沒有使用函數(shù),而是直接給函數(shù)內(nèi)部的代碼 粘貼 到了原函數(shù)處,這大大增加了可執(zhí)行文件的內(nèi)存占用量。
除此之外,內(nèi)聯(lián)函數(shù)還有很重要的一個缺點:聲明和定義不能分離在兩個文件中,會導(dǎo)致鏈接失敗。
值得注意的一點是,在有些編譯器(如vs2022)中,并非程序員添加inline他就變成了內(nèi)聯(lián)函數(shù),這得編譯器的心情(其實就是debug下,編譯器會識別內(nèi)聯(lián)函數(shù)的代碼量,如果代碼量很大,就不會展開函數(shù),而是作為一般函數(shù)調(diào)用)
如果想要調(diào)試的話,按照下方的操作可以在debug模式下調(diào)試:
- 菜單欄 - 項目 - 屬性


三、 nullptr
NULL實際是?個宏,在傳統(tǒng)的C頭?件(stddef.h)中,可以看到如下代碼:
#ifndef NULL #ifdef __cplusplus #define NULL 0 #else #define NULL ((void *)0) #endif #endif
- 也就是說,在C++里面
NULL,被定義為了0,是一個int類型。 - C++11中引?
nullptr,nullptr是?個特殊的關(guān)鍵字,nullptr是?種特殊類型的字?量,它可以轉(zhuǎn)換成任意其他類型的指針類型。使?nullptr定義空指針可以避免類型轉(zhuǎn)換的問題,因為nullptr只能被隱式地轉(zhuǎn)換為指針類型,?不能被轉(zhuǎn)換為整數(shù)類型。
從此以后,C++里面的空指針使用
nullptr,不使用NULL。
總結(jié)
到此這篇關(guān)于C++引用、內(nèi)聯(lián)函數(shù)與nullptr的文章就介紹到這了,更多相關(guān)C++引用、內(nèi)聯(lián)函數(shù)與nullptr內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- C++入門(命名空間,缺省參數(shù),函數(shù)重載,引用,內(nèi)聯(lián)函數(shù),auto,范圍for)
- C++中引用、內(nèi)聯(lián)函數(shù)、auto關(guān)鍵字和范圍for循環(huán)詳解
- C++類與對象深入之引用與內(nèi)聯(lián)函數(shù)與auto關(guān)鍵字及for循環(huán)詳解
- C++命名空間?缺省參數(shù)?const總結(jié)?引用總結(jié)?內(nèi)聯(lián)函數(shù)?auto關(guān)鍵字詳解
- C++示例分析內(nèi)聯(lián)函數(shù)與引用變量及函數(shù)重載的使用
- C++?引用與內(nèi)聯(lián)函數(shù)詳情
- 一步步從底層入手搞定C++引用與內(nèi)聯(lián)函數(shù)
相關(guān)文章
基于c++11的event-driven library的理解
這篇文章主要介紹了基于c++11的event-driven library的理解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-02-02
C++學(xué)了這么多年你知道為什么定義類時,類的定義放在.h文件中,而類的實現(xiàn)放在cpp文件中。它們?yōu)槭裁茨軌蜿P(guān)聯(lián)到一起呢?你知道什么東西可以放在.h文件中,什么不能。什么東西又可以放在cpp文件中。如果你忘記了或是壓根就不明白,那么讀過此文你會清晰無比?。?/div> 2014-09-09
C++ Qt開發(fā)之ComboBox下拉組合框組件用法詳解
Qt 是一個跨平臺C++圖形界面開發(fā)庫,利用Qt可以快速開發(fā)跨平臺窗體應(yīng)用程序,在Qt中,ComboBox(組合框)是一種常用的用戶界面控件,它提供了一個下拉列表,允許用戶從預(yù)定義的選項中選擇一個,本文給大家介紹QComboBox類的一些常用方法,需要的朋友可以參考下2023-12-12
用C++類實現(xiàn)單向鏈表的增刪查和反轉(zhuǎn)操作方法
下面小編就為大家?guī)硪黄肅++類實現(xiàn)單向鏈表的增刪查和反轉(zhuǎn)操作方法。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-04-04
vs2022項目文件夾內(nèi).vs文件夾容量虛高問題的解決
經(jīng)常會發(fā)現(xiàn)VS的項目文件夾占用空間很大,本文主要介紹了vs2022項目文件夾內(nèi).vs文件夾容量虛高問題的解決,具有一定的參考價值,感興趣的可以了解一下2023-09-09最新評論

