引入
??哈嘍各位鐵汁們好啊,我是博主鴿芷咕《C++干貨基地》是由我的襄陽家鄉零食基地有感而發,不知道各位的城市有沒有這種實惠又全面的零食基地呢?C++ 本身作為一門篇底層的一種語言,世面的免費課程大多都沒有教明白。所以本篇專欄的內容全是干貨讓大家從底層了解C++,把更多的知識由抽象到簡單通俗易懂。
?? 推薦
前些天發現了一個巨牛的人工智能學習網站,通俗易懂,風趣幽默,忍不住分享一下給大家。點擊跳轉到網站。
文章目錄
- 引入
- ?? 推薦
- 一、內聯函數
- 1.1 內聯函數的概念
- 1.2 內聯函數的特性
- 內聯函數適合每個函數都用嗎?
- 內聯函數需要聲明和定義分離嗎?
- 二、auto關鍵字
- 2.1 auto 的使用場景
- 2.2 auto 不能推導的場景
- 三、基于范圍for循環
- 3.1 范圍for的語法
- 3.2 范圍for 的注意事項
- 四、指針空值 nullptr
- 4.1 C++98中的指針空值
- 4.2 nullptr的由來
- 📝文章結語:
一、內聯函數
1.1 內聯函數的概念
以往我們在C語言中實現比較簡單的函數來說都是用宏來實現的,比如說實現一個加法,但是用宏實現的小型函數有很多缺點:
- 第一點就是宏經常容易寫錯,末尾的引號問題和運算符優先級問題等等
- 第二點就是宏他并沒有類型安全檢查就算是一個加法也有可能有人給你傳倆個字符
- 第三點就是宏不方便調試,導致代碼可讀性差
所以在C++中就采用了內聯函數和枚舉來解決宏的使用的問題
以inline修飾的函數叫做內聯函數,編譯時C++編譯器會在調用內聯函數的地方展開,沒有函數調
用建立棧幀的開銷,內聯函數提升程序運行的效率。
1.2 內聯函數的特性
內聯函數是以inline修飾的函數,在調用其該函數的時候會直接在調用處展開并不會開辟函數的棧幀空間所以非常適用在一下短小函數上面:
下面就給大家來看一下使用內聯函數的效果:
🍸 代碼一:
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;int Add(int x, int y)
{int ret = x + y;return ret;
}int main()
{int ret = Add(1,2);return 0;
}
🍸 代碼二:
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;inline int Add(int x, int y)
{int ret = x + y;return ret;
}int main()
{int ret = Add(1,2);return 0;
}
這里我們就可以看到只要函數我們加了 inline
關鍵字在調用的時候就直接展開不會開辟新的棧幀空間然后去 call
調用它。
內聯函數適合每個函數都用嗎?
內聯函數看起來不用開辟函數的棧幀空間大大結束了效率但是每個短小的函數都適合使用內聯函數嗎?
- 其實函數在調用次數過多的情況下就不適合使用內聯函數,這樣就會導致代碼膨脹到處都是重復的代碼,從而使得可執行程序變大;
- 還有在函數的遞歸時也不能使用內聯函數,函數棧幀是可以復用的,但內聯函數一旦使用也會導致代碼膨脹
🔥 注: inline對于編譯器而言只是一個建議,不同編譯器關于inline實現機制可能不同,一般建
議:將函數規模較小(即函數不是很長,具體沒有準確的說法,取決于編譯器內部實現)、不
是遞歸、且頻繁調用的函數采用inline修飾,否則編譯器會忽略inline特性。下圖為
《C++prime》第五版關于inline的建議:
內聯函數需要聲明和定義分離嗎?
inline不建議聲明和定義分離,分離會導致鏈接錯誤。因為inline被展開,就沒有函數地址
了,鏈接就會找不到。
二、auto關鍵字
auto 關鍵字聽聽名字就非常簡單,是C++11的時候,標準委員會賦予了auto全新的含義即可以自動推導變量類型使用起來也十分簡單:
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;int main()
{int a = 10;auto p1 = a;auto* p2 = &a;auto& p3 = a;return 0;
}
這里auto后面加的 * 號就代表指針,&和以前一樣是引用是我們指定自動類型。
🔥 注:使用auto定義變量時必須對其進行初始化,在編譯階段編譯器需要根據初始化表達式來推導auto的實際類型。
因此auto并非是一種“類型”的聲明,而是一個類型聲明時的“占位符”,編譯器在編譯期會將auto替換為變量實際的類型。
2.1 auto 的使用場景
auto 關鍵的意義其實并不是像我們前面那樣去使用,是針對特別復雜的類型配合使用的比如:
🍸 場景一:
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;int fun(int x, int y)
{int ret = x + y;return ret;
}
int main()
{int(*fp1)(int, int) = fun;auto fp2 = fun;return 0;
}
🍸 場景一:
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<map>
#include<string>
using namespace std;int main()
{std::map<std::string, std::string> dict;std::map<std::string, std::string>::iterator it = dict.begin();auto it = dict.begin();return 0;
}
2.2 auto 不能推導的場景
- auto不能作為函數的參數
// 此處代碼編譯失敗,auto不能作為形參類型,因為編譯器無法對a的實際類型進行推導
void TestAuto(auto a)
{}
- auto不能直接用來聲明數組
void TestAuto()
{int a[] = {1,2,3};auto b[] = {4,5,6};
}
三、基于范圍for循環
在以前只要遍歷數組我們就可能想到,使用for循環來遍歷數組比如這樣:
void TestFor()
{
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;
}
但是使用這種方法遍歷數組太麻煩了,而且還不好寫比較繁瑣所以在C++11 中新增加了范圍 for 的概念
3.1 范圍for的語法
for循環后的括號由冒號“ :”分為兩部分:
- 第一部分是范圍內用于迭代的變量
- 第二部分則表示被迭代的范圍。
void TestFor()
{int array[] = { 1, 2, 3, 4, 5 };for(auto& e : array)e *= 2;for(auto e : array)cout << e << " ";
}
在這里就巧妙的運用了我們上面講的 auto 關鍵字來自動識別數組元素的類型:
- e 在這里是數組元素的臨時拷貝所以我們如果想要改變數組元素
- 就得指定自動類型為引用,去用于改變數組元素
3.2 范圍for 的注意事項
🔥 for循環迭代的范圍必須是確定的
- 對于數組而言,就是數組中第一個元素和最后一個元素的范圍;
- 對于類而言,應該提供begin和end的方法,begin和end就是for循環迭代的范圍。
例如以下代碼就是錯誤的:
- 這里我們并不明確因為for的范圍不確定
void TestFor(int array[])
{for(auto& e : array)cout<< e <<endl;
}
四、指針空值 nullptr
4.1 C++98中的指針空值
一般我們定義變量好的習慣是每一個變量都初始化值但是 C++98 中 祖師爺在定義 NULL 指針空值的時候是這樣定義的:
#ifndef NULL
#ifdef __cplusplus
#define NULL 0
#else
#define NULL ((void *)0)
#endif
#endif
這就導致了一個問題大家看一下下面這個代碼結果你們猜是什么呢?
void f(int)
{cout << "f(int)" << endl;
}
void f(int*)
{cout << "f(int*)" << endl;
}
int main()
{f(0);f(NULL);f((int*)NULL);return 0;
}
誒這里使用指針 NULL 定義空值的時候就出現問題了,所以在C++11中新增了一個關鍵字來填這個缺陷
4.2 nullptr的由來
nullptr 的由來就是因為祖師爺在一開始定義 NULL是使用宏定義這就導致
- NULL 被替換成 0 了,而不是((void *)0);
- 所以新增了一個關鍵字 nullptr == ((void *)0);
注意:
-
在使用nullptr表示指針空值時,不需要包含頭文件,因為nullptr是C++11作為新關鍵字引入的。
-
在C++11中,sizeof(nullptr) 與 sizeof((void*)0)所占的字節數相同。
-
為了提高代碼的健壯性,在后續表示指針空值時建議最好使用nullptr。
📝文章結語:
?? 看到這里了還不給博主扣個:
?? 點贊
🍹收藏
?? 關注
!
💛 💙 💜 ?? 💚💓 💗 💕 💞 💘 💖
拜托拜托這個真的很重要!
你們的點贊就是博主更新最大的動力!