隨著c++11的發布,c++這門語言有了本質上的提公升。c++14,c++17的相繼推出,更是讓c++這門語言達到了乙個新高度。新的標準庫設施,新的語法,讓我們得以書寫更加安全、便捷、高效的程式。
2023年6月程式語言排行榜:
那麼這些新的語法究竟是什麼?它們如何使用?能為我們程式設計帶來哪些便利?這便是本文所**的。
新的空指標型別——nullptr
適用度:★★★★★
nullptr是一種特殊的字面值,它可以轉化為任意一種指標型別。 原來我們初始化乙個空指標都是直接將他賦值為null,但null實際上是乙個巨集,其值相當於0。
編譯器是這麼定義null的:
#ifdef __cplusplus //如果定義__cplusplus巨集,說明正在編譯c++語言
#define null 0
#else
#define null ((void *)0)
#endif
也許你會想「我們用null還不是照樣吊打集訓隊」,nullptr好像並沒有什麼用。
考慮這樣一段**:
int f(int x)
int f(int *x)
int main()
很顯然,編譯失敗,對f的呼叫有二義性。因為null相當於0,既可轉化為指標,也可轉化為整形。將null換做nullptr即可,nullptr便是為了解決這種二義性的問題而誕生的。
條件允許的前提下,盡量使用nullptr,它比null更加安全, 原來這樣寫:
int a = null;
int *b = null;
現在應該這樣寫:
int a = null;
int *b = nullptr;
避免奇葩錯誤——constexpr變數
適用度:★★★★☆
在程式設計中,我們經常遇到需要定義常量的情況,但有些常量卻並不是你所想的「常量」。因而會引發一些意想不到的錯誤。
例如:int a;
const int b = a + 10;
int c[b];//錯誤,b不是乙個常量表示式,它的值每次執行都有可能不一樣。
b的確是**乙個常量——它的值在程式的執行期間不會被修改**,但是**它並不是常量表示式——每次執行程式時都為同乙個值,且程式執行期間無法被修改。**
使用**constexpr**而非const來宣告常量,讓編譯器來幫你檢查常量是不是每次程式執行都為同乙個值。
int a;
constexpr b = a + 10;//錯誤!a不是常量表示式!
int a;
const int b = a + 10;
constexpr int c = b + 10;//錯誤,b為常量,但不是常量表示式!
const int a = 10;
const int b = a + 10;
constexpr int c = b + 10;//正確
省事好幫手——auto型別指示符
適用度:★★★★★
有些型別名字太長,難以拼寫,浪費時間。怎麼辦?
知道函式的作用,卻無法拼寫其返回型別,無法儲存其返回值。怎麼辦?
這個時候auto型別指示符就能夠助我們一臂之力了。
原來我們這麼寫:
vectorvec;
for(vector::iterator it = vec.begin();it != vec.end();it++)//do something
現在可以簡單的這麼寫:
vectorvec;
for(auto it = vec.begin();it != vec.end();it++)//do something
怎麼樣?程式瞬間清爽了許多有木有。而且還可以節約大量寶貴的時間
因為編譯器是依靠初始值來推斷auto變數的型別的,所以auto變數必須要有初始值。
即使是這樣也不行:
auto a;
a = 10;
當然,也不能用auto來定義陣列
auto和引用一起會產生一些奇怪的問題:
int i = 1,&r = i;//定義變數i,r為i的引用
auto p = r;//沒錯,p的值為int,其值為1
為什麼?因為引用即別名。正如我們熟知的,使用引用其實是使用引用的物件,特別當引用被用作初始值的時候,真正參與初始化的其實是引用物件的值。此時編譯器以引用物件的型別作為auto的型別。
自動型別推斷——decltype型別指示符
適用度:★★★★☆
上文提到了auto的用法,有時候我們想要用表示式的型別初始化乙個變數,卻並不想用表示式的值初始化這個變數。這個時候decltype型別指示符就可以派上用場了。
劇透:下文位置返回型別配合decltype型別指示符有驚喜
我們可以這樣用decltype型別指示符來定義變數:
int a = 10;
decltype(a) b;
b = 20;
但是要注意,decltype只會用表示式的返回值進行推斷,並不會執行表示式。例如:
int f()
decltype(f()) i = 123;//i的值為123
//程式執行並不會有任何輸出,因為f函式並沒有實際執行。
int i = 1;
decltype(i = 123) b = i;
cout << i << endl;//輸出1,因為i = 42表示式並未實際執行
decltype和auto都可以完成型別推斷的任務,那麼它們有什麼不同呢?
1.處理引用
int i = 1,&r = i;//定義int型變數i,r為i的引用。
auto a = r;//此時a的型別為int
decltype(r) b = r;//此時b的型別為int&,即為int的引用。
2.處理頂層const
這裡引入乙個概念:
1.底層const,物件所指向的物件是const的。 2.頂層const,物件本身是const的。
auto會忽略掉頂層const和引用,但是會保留底層const。
const int ci = i,&cr = ci;
auto a = ci;//a為int,頂層const被忽略
auto b = cr;//b為int,頂層const和引用均被忽略
auto c = &ci;//c為指向常量int的指標,保留底層const
如果要使auto型別為頂層const:
int i = 1;
const auto a = i;//a為const int 型別
如果decltype使用的表示式是乙個變數,decltype會返回該變數的型別(包括引用和頂層const)。
迴圈巨集的優秀替代品——範圍for語句
適用度:★★★★★
什麼?就算有了auto型別指示符,遍歷容器/陣列每乙個元素你還是嫌麻煩?沒事,讓範圍for語句來幫你。
原來這麼遍歷容器/陣列每乙個元素
vectorvec;
for(auto it = vec.begin();it != vec.end();it++)
cout << *it << " ";
現在這麼寫:
vectorvec;
for(auto it : vec)
cout << it << " ";
注意,範圍for語句只能遍歷每乙個元素,所以像遍歷1到10這種操作還是得自己乖乖寫for迴圈:)。
複雜返回值必備——尾置返回型別
適用度:★★★★☆
普通函式完全不必要尾置返回型別,但是當函式返回型別複雜起來時,尾置返回型別就很有用了。
int (*func(int i))[10]
//func(int i)表示呼叫函式時,需要乙個int型別的引數;
//(*func(int i))表示對呼叫func的結果執行解引用的操作;
//(*func(int i))[10]表示解引用之後得到乙個維度為10的陣列;
//int (*func(int i))[10]表示陣列的資料型別為int;
很複雜,對吧?(當然對於dalao來說小菜一碟)當返回型別更加複雜時,常規寫法將會成為debug噩夢。(話說markdown好像識別不了尾置返回型別誒)。
//返回一維陣列
auto func(int i) -> int(*)[10]
還有更複雜的(我太蒻了給不出常規寫法了)
二維陣列:
auto func(int i) -> int(*)[10][10]
二重指標:
auto func(int i) -> int **
除了陣列特殊一些以外,平時定義變數怎麼寫,尾置返回型別就怎麼寫。程式瞬間清爽了許多有木有。
如果返回值更加複雜,連尾置返回型別的作用都顯得微乎其微了怎麼辦?這時候——
配合decltype食用效果更佳
auto func(int a,int b) -> decltype(a+b)
什麼?你連尾置返回型別都嫌麻煩?c++14可以滿足你的需求。沒錯,連尾置返回型別都可以省了,直接返回型別auto就可以了orz。
你不知道的 和
開發中,編寫有一定逼格的 是每個程式猿都追求的。經常用來判斷的符號 和 也經常用來定義變數哦,你知道嗎?邏輯與 在有乙個運算元不是布林值的情況下,就不一定返回布林值。比如以下情況 1 第乙個運算元是物件,返回第二個數 var myinfo console.log myinfo 2 輸出22 第二個運...
你不知道的box shadow
我們可以僅使用乙個div利用shadow配合animation實現很多豐富的效果 github 求 必需。水平陰影的位置。允許負值。v shadow 必需。垂直陰影的位置。允許負值。blur 可選。模糊距離。spread 可選。陰影的尺寸。color 可選。陰影的顏色。請參閱 css 顏色值。ins...
你不知道的 gitignore
乙個.gitignore檔案顯式地指定了哪些檔案不應被git追蹤,即被git忽略掉。在被gitignore之前已經被git追蹤的檔案不受gitignore規則的影響。關於gitignore規則的詳情請繼續往下看。gitignore檔案中的每一行都指定了一種匹配模式。通常來說,git會從多個可能的規則...