目錄
1、引用
1.1 引用概念
1.2 引用特性
1.3 常引用
1.4 使用場景
1.4.1 引用做參數
?1.4.2 引用做返回值
1.5?引用和指針的區別
2、內聯函數?
2.1 概念
2.2 特性
3、auto關鍵字?
4、基于范圍的for循環?
5、指針空值nullptr?
5.1 C++98 中的指針空值處理
5.2?C++11 中 nullptr 的優勢
? 本章節將延續上篇文章未講完的內容,期待接下來的內容哦!!!
1、引用
1.1 引用概念
類型& 引用變量名(對象名) = 引用實體; void TestRef() {int a = 10;int& ra = a;//<====定義引用類型printf("%p\n", &a);printf("%p\n", &ra); }
? ? ?ra是a的別名,是一個變量,占用同一塊內存
? ? 注意: 引用類型 必須和引用 實體 是 同種類型 的1.2 引用特性
? ? 1. 引用在 定義時必須初始化? ? 2. 一個變量可以有多個引用? ? 3. 引用一旦引用一個實體,再不能引用其他實體![]()
![]()
![]()
1.3 常引用
? ? 只有引用才涉及權限的變化
1.4 使用場景
1.4.1 引用做參數
? ? 輸出型參數:形參的改變會改變實參
? ? 引用做參數不會開辟空間
?1.4.2 引用做返回值
? ? 引用做返回值不會創建臨時變量
1.5?引用和指針的區別
? ? 引用語法層面不開空間,底層實現和指針類似
面試常考點:
2、內聯函數?
2.1 概念
? ? 以 inline 修飾 的函數叫做內聯函數, 編譯時 C++ 編譯器會在 調用內聯函數的地方展開 ,沒有函數調用建立棧幀的開銷,內聯函數提升程序運行的效率。
? ? 如果在上述函數前增加inline關鍵字將其改成內聯函數,在編譯期間編譯器會用函數體替換函數的調用查看方式:1. 在 release 模式下,查看編譯器生成的匯編代碼中是否存在 call Add2. 在 debug 模式下,需要對編譯器進行設置,否則不會展開 ( 因為 debug 模式下,編譯器默認不會對代碼進行優化,以下給出vs2022 的設置方式 )![]()
![]()
2.2 特性
1. inline 是一種 以空間換時間 的做法,如果編譯器將函數當成內聯函數處理,在 編譯階段,會用函數體替換函數調用 ,缺陷:可能會使目標文件變大,優勢:少了調用開銷,提高程序運行效率。2. inline 對于編譯器而言只是一個建議,不同編譯器關于 inline 實現機制可能不同 ,一般建議:將 函數規模較小 ( 即函數不是很長,具體沒有準確的說法,取決于編譯器內部實現 ) 、 不是遞歸、且頻繁調用 的函數采用 inline 修飾,否則編譯器會忽略 inline特性。下圖為《 C++prime 》第五版關于 inline 的建議:
? ? ?問題: 為啥內聯函數可能會導致目標文件變大
3. inline 不建議聲明和定義分離,分離會導致鏈接錯誤。因為 inline 被展開,就沒有函數地址了,鏈接就會找不到![]()
// F.h #include <iostream> using namespace std; inline void f(int i); // F.cpp #include "F.h" void f(int i) {cout << i << endl; } // main.cpp #include "F.h" int main() {f(10);return 0; } // 鏈接錯誤:main.obj : error LNK2019: 無法解析的外部符號 "void __cdecl f(int)" (?f@@YAXH@Z),該符號在函數 _main 中被引用
內聯函數和宏函數? ? 相似點:?
? ? 避免函數調用開銷:都能在一定程度上避免常規函數調用時的棧幀創建、參數傳遞等開銷,提高程序運行效率。
? ? 代碼替換:在編譯或預處理階段,都會將相關代碼替換到調用處。內聯函數由編譯器決定是否展開替換,宏函數是在預處理階段進行文本替換。?
? ? 優缺點
? ? 內聯函數:優點是有類型檢查和語法檢查,增強了程序的健壯性,且調試方便;缺點是編譯器對其展開有條件限制,當函數體復雜時可能不進行內聯,同時會使代碼體積增大。
? ? 宏函數:優點是簡單靈活,可定義復雜的表達式,在代碼生成方面有一定的優勢;缺點是沒有類型檢查,容易出現副作用,且在調試時難以定位問題,也可能導致代碼可讀性變差。
3、auto關鍵字?
? ? 使用 auto 定義變量時必須對其進行初始化,在編譯階段編譯器需要根據初始化表達式來推導 auto 的實際類型 。因此 auto 并非是一種 “ 類型 ” 的聲明,而是一個類型聲明時的 “ 占位符 ” ,編譯器在編 譯期會將 auto 替換為變量實際的類型 。
4、基于范圍的for循環?
void TestFor1() {int array[] = { 1, 2, 3, 4, 5 };//讓數組中的元素大小變成原來的二倍for (int i = 0; i < sizeof(array) / sizeof(array[0]); ++i)array[i] *= 2;//打印數組元素for (int* p = array; p < array + sizeof(array) / sizeof(array[0]); ++p)cout << *p << endl; }void TestFor2() {int array[] = { 1, 2, 3, 4, 5 };//讓數組中的元素大小變成原來的二倍for (auto& e : array)e *= 2;//打印數組元素for (auto e : array)cout << e << " ";}int main() {TestFor2();return 0; }
5、指針空值nullptr?
5.1 C++98 中的指針空值處理
初始化方式
? ? 在 C/C++ 編程里,為避免未初始化指針帶來的錯誤,習慣給指針合適初值。在 C++98 中,當指針無合法指向時,常見初始化方式:
NULL的本質
NULL 本質是宏,在傳統 C 頭文件?
stddef.h
?中定義如下:
即 NULL 可能被定義為字面常量 0 ,或無類型指針?
(void*)
?的常量 。使用 NULL 的麻煩
? ? 這里?
f(0)
?調用?f(int)
?沒問題,但?f(NULL)
?由于 NULL 定義的模糊性(既像 0 又像指針),可能導致編譯器匹配混亂,而?f((int*)NULL)
?雖然明確轉化為指針類型調用?f(int*)
?,但這種寫法不夠簡潔直觀 。5.2?C++11 中 nullptr 的優勢
無需額外頭文件
? nullptr
?是 C++11 引入的新關鍵字,專門表示指針空值 。使用它時,無需包含額外頭文件,代碼簡潔性提升。字節數特性
? ? 在 C++11 中,sizeof(nullptr)
?與?sizeof((void*)0)
?所占字節數相同 。這意味著?nullptr
?在內存占用等底層特性上,和傳統表示空指針的方式在字節層面有對應關系。提升代碼健壯性
? ? 相比?NULL
?可能帶來的歧義,nullptr
?明確表示指針空值。在函數重載等場景下,能讓編譯器準確匹配函數,減少錯誤發生概率,使代碼更健壯。例如之前的?f
?函數調用,使用?nullptr
?就很明確:? ? 總結來說,
nullptr
?作為 C++11 的新特性,解決了 C++98 中?NULL
?表示指針空值的一些弊端,讓指針空值的表達更清晰、準確,有助于寫出更可靠的代碼