隨著C++11的發布,C++這門語言有了本質上的提升。C++14和C++17的相繼推出,更是讓C++這門語言達到了一個新高度。新的標準庫設施,新的語法,讓我們得以書寫更加安全、便捷、高效的程序。
2018年6月程式語言排行榜:
那麼這些新的語法究竟是什麼?它們如何使用?能為我們編程帶來哪些便利?這便是本文所探討的。
本文參考部分資料,文末已給出原文章地址。
新的空指針類型——nullptr
適用度:★★★★★
nullptr是一種特殊的字面值,它可以轉化為任意一種指針類型。原來我們初始化一個空指針都是直接將他賦值為NULL,但NULL實際上是一個宏,其值相當於0。
編譯器是這麼定義NULL的:
也許你會想「我們用NULL還不是照樣吊打集訓隊」,nullptr好像並沒有什麼用。
考慮這樣一段代碼:
很顯然,編譯失敗,對f的調用有二義性。因為NULL相當於0,既可轉化為指針,也可轉化為整形。將NULL換做nullptr即可,nullptr便是為了解決這種二義性的問題而誕生的。
條件允許的前提下,儘量使用nullptr,它比NULL更加安全,原來這樣寫:
現在應該這樣寫:
避免奇葩錯誤——constexpr變量
適用度:★★★★☆
在編程中,我們經常遇到需要定義常量的情況,但有些常量卻並不是你所想的「常量」。因而會引發一些意想不到的錯誤。
例如:
i
b的確是一個常量——它的值在程序的執行期間不會被修改,但是它並不是常量表達式——每次執行程序時都為同一個值,且程序執行期間無法被修改。
使用constexpr而非const來聲明常量,讓編譯器來幫你檢查常量是不是每次程序執行都為同一個值。
省事好幫手——auto類型指示符
適用度:★★★★★
有些類型名字太長,難以拼寫,浪費時間。怎麼辦?
知道函數的作用,卻無法拼寫其返回類型,無法保存其返回值。怎麼辦?
這個時候auto類型指示符就能夠助我們一臂之力了。
原來我們這麼寫:
現在可以簡單的這麼寫:
怎麼樣?程序瞬間清爽了許多有木有。而且還可以節約大量寶貴的時間
因為編譯器是依靠初始值來推斷auto變量的類型的,所以auto變量必須要有初始值。
即使是這樣也不行:
當然,也不能用auto來定義數組
auto和引用一起會產生一些奇怪的問題:
為什麼?因為引用即別名。正如我們熟知的,使用引用其實是使用引用的對象,特別當引用被用作初始值的時候,真正參與初始化的其實是引用對象的值。此時編譯器以引用對象的類型作為auto的類型。
自動類型推斷——decltype類型指示符
適用度:★★★★☆
上文提到了auto的用法,有時候我們想要用表達式的類型初始化一個變量,卻並不想用表達式的值初始化這個變量。這個時候decltype類型指示符就可以派上用場了。
劇透:下文位置返回類型配合decltype類型指示符有驚喜
我們可以這樣用decltype類型指示符來定義變量:
但是要注意,decltype只會用表達式的返回值進行推斷,並不會執行表達式。例如:
decltype和auto都可以完成類型推斷的任務,那麼它們有什麼不同呢?
1.處理引用
2.處理頂層const
這裡引入一個概念:
1.底層const,對象所指向的對象是const的。2.頂層const,對象本身是const的。
auto會忽略掉頂層const和引用,但是會保留底層const。
如果要使auto類型為頂層const:
如果decltype使用的表達式是一個變量,decltype會返回該變量的類型(包括引用和頂層const)。
循環宏的優秀替代品——範圍for語句
適用度:★★★★★
什麼?就算有了auto類型指示符,遍歷容器/數組每一個元素你還是嫌麻煩?沒事,讓範圍for語句來幫你。
原來這麼遍歷容器/數組每一個元素
現在這麼寫:
注意,範圍for語句只能遍歷每一個元素,所以像遍歷1到10這種操作還是得自己乖乖寫for循環:)。
複雜返回值必備——尾置返回類型
適用度:★★★★☆
普通函數完全不必要尾置返回類型,但是當函數返回類型複雜起來時,尾置返回類型就很有用了。
很複雜,對吧?(當然對於dalao來說小菜一碟)當返回類型更加複雜時,常規寫法將會成為Debug噩夢。(話說Markdown好像識別不了尾置返回類型誒)。
還有更複雜的(我太蒻了給不出常規寫法了)
二維數組:
二重指針:
除了數組特殊一些以外,平時定義變量怎麼寫,尾置返回類型就怎麼寫。程序瞬間清爽了許多有木有。
如果返回值更加複雜,連尾置返回類型的作用都顯得微乎其微了怎麼辦?這時候——
配合decltype食用效果更佳
什麼?你連尾置返回類型都嫌麻煩?C++14可以滿足你的需求。沒錯,連尾置返回類型都可以省了,直接返回類型auto就可以了Orz。
命名困難戶/裝逼者的寵兒——Lambda表達式
適用度:★★★☆☆
假如遇到一道毒瘤題,既需要從小到大排序,也需要從大到小排序,甚至還要給自己定義的結構體排序。難道排序函數依次叫做cmp1,cmp2,cmp3?太沒有逼格了吧
一個完整的Lambda表達式由以下幾個部分構成:
各項具體含義如下
1.capture list:捕獲外部變量列表 可以為空,但是不可以省略
2.params list:形參列表 可以為空,但是不可以省略
3.mutable指示符:用來說用是否可以修改捕獲的變量 可以省略
4.exception:異常設定 可以省略
5.return type:返回類型 可以省略
6.function body:函數體 可以為空,但是不可以省略
太複雜了,對吧?實際上,OI中我們使用Lambda表達式主要是用於STL的謂詞(比如排序),因而我們可以省略很多不必要的部分。
該省略的省略後就十分簡單了:比如從大到小排序:
Lambda表達式看似複雜,卻能在許多時候為我們提供不小便利。它也是函數式編程的基石。
因考慮篇幅,Lambda表達式並未詳細介紹。想要知道更多關於Lambda表達式的內容,可以看看我的另一篇文章。傳送門
鳴謝:
本文參考了以下資料,感謝作者的辛勞付出:
https://www.jianshu.com/p/2d44dae53910
https://blog.csdn.net/zdy0_2004/article/details/69934828
https://www.cnblogs.com/DswCnblog/p/5629165.html
https://blog.csdn.net/y1196645376/article/details/51441503
注:因C++11語法繁雜,有些高級特性只為大型工程而設計,對OI並無太大幫助,因而未能出現在文章中(如繼承,多態,泛型編程)等等。本人水平有限,文章難免有錯誤,望讀者多多海涵,可以評論指出錯誤,一定盡力修正。
本文發布於洛穀日報,特約作者:colazcy
原文地址:https://www.luogu.org/blog/64456/ni-fou-zhi-dao-di-c-yu-fa