讀了《C++ 的門門道道 | 技術頭條》這篇文章以後有不少共鳴,能夠說是近期看過的最好的技術 tips 文章了。按照這篇文章裏面講到的幾點,結合工做上實際遇到的問題,我也來講一下個人感覺。html
<!-- more -->c++
成員變量忘了初始化是一個至關經典的錯誤,甚至《Effective C++》中還專門列了一條來說這個事情。在工做中,我就看到過這種錯誤,同事對一個新增的功能加上了開關控制的邏輯,可是忘了對這個開關的標識進行初始化,致使了一條分支邏輯失效。並且由於 C++ 沒有默認初始值,那它的初始值是隨機的,因此致使線上的表現是機率性復現,增長了 debug 的難度。固然增長 Coverity 掃描提前發現這個問題,當時項目上線比較急就直接跳過了這一步。程序員
從 C++11 開始支持在聲明成員變量的時候直接初始化,有了這個特性以後,我已經養成了全部成員變量都直接在聲明的時候初始化。算法
class Ad { private: unsinged int lifetime = 10000; };
這個坑即使是有點經驗的程序員也會踩到,有一次線上事故就是一個穩定跑了好久的邏輯,忽然出現了 core,並且是持續地 core 在 sort 上。花了很長時間排查,最後才意識到實現新增的 sort 比較函數沒有保證嚴格弱序(strict weak order),比較兩個對象的屬性時用了 <=
。這裏就涉及到 C++ 中 sort 的實現。細節以後會寫一篇文章來說,簡單說來就是 STL sort 核心排序算法是快排,在依據 pivot 調整元素位置時採用的實現方式以下:shell
while (true) { while (__comp(*__first, __pivot)) ++__first; --__last; while (__comp(__pivot, *__last)) --__last; if (!(__first < __last)) return __first; std::iter_swap(__first, __last); ++__first; }
重點就在於 while (__comp(*__first, __pivot)) ++__first;
,當整個容器裏的元素都相等時,就會致使 __first 這個迭代器越界,程序就 core 了。函數
原文關於如何避免操做符短路講得很好了,其實咱們還能夠利用操做符短路來簡化代碼。對我更經常使用的場景:if (!stack.empty() && stack.top() == 0)
,這偏偏是利用短路來合併判斷。性能
這個有個經典場景,在 vector 裏面,我要找到首個遞增序列的最後一個元素,很容易寫成這樣的代碼:優化
while (i < ve.size() - 1 && ve[i] <= ve[i + 1]) i++;
這裏若是傳入的 ve 是個空 vector,那麼就會成爲超大循環,由於 vector::size()
返回的是 unsigned int
,根據數值類型傳遞,ve.size() - 1
的類型也是 unsigned int
,那麼就會返回一個很大的數,致使 while
陷入超大循環。spa
vector 能夠說是在平常開發中使用頻率最高的容器了,支持下標訪問,動態擴容,二分查找的效率,C++11 以後支持移動構造,這些優勢都讓它很是好用。vector 的坑都集中在它的動態擴容上,理解它動態擴容的機制能夠在開發中避開這些坑。debug
vector 動態擴容的兩個特色:
根據這兩個特色,結合 vector 的其餘特性獲得的:
組裏有一個項目升級到 C++11 以後,一窩蜂地使用 unordered_map,可是其實對於小數據量,好比本次請求命中的一些配置,其實數據量基本都在 10 項之內,那其實用 map 就徹底夠用了,unorder_map 查找的效率固然是高的,可是也要認識到它維護一個哈希表額外付出的性能代價。
由於一開始設計的數值類型過於嚴格而致使的重寫,我遇到過不止一次了。
有些人寫代碼的時候有一種傾向,就是能省則省,能用 int 的毫不用 long,能用 short 的毫不用 int。可是其實有些狀況下 short 並不能節省空間(字節對齊),還致使過分「優化」,致使邏輯變化以後要重寫,或者實際的取值不符合設計致使溢出。
什麼是「箭頭型代碼」?見下圖:
這種代碼其實在業務複雜的場景下並很多見,酷殼上有一篇文件專門講過如何重構這種代碼:《如何重構「箭頭型」代碼》。
我在實際項目中應用比較可能是利用 while (0) 來規避這種代碼。
在項目常常遇到的場景是對一連串條件進行判斷,不符合條件的分支須要打印日誌,示例代碼以下:
if (conditionA()) { if (conditionB()) { if (conditionC()) { if (conditionD()) { // do something } else { // log } } else { // log } } else { // log } } else { // log }
這種狀況下用 do-while(0)
能夠進行很是好的重構,重構以後的代碼以下:
do { if (!conditionA()) { // log break; } if (!conditionB()) { // log break; } if (!conditionC()) { // log break; } if (!conditionD()) { // log break; } } while (0);