每天學點C++知識:儘可能使用列舉類

JingerJoe發表於2016-01-10

靜態程式碼分析工具可簡化編碼過程,檢測出錯誤並幫助修復。PVS-Studio 是一個用於 C/C++ 的靜態程式碼分析工具。該團隊檢測了 200 多個 C/C++ 開源專案,包括了 Unreal Engine、Php、Haiku、Qt 和 Linux 核心等知名專案。於是他們每天分享一個錯誤案例,並給出相應建議。伯樂線上翻譯組正在翻譯這個系列,今天是第一篇。


下面這個 Bug 是在 Source SDK 的原始碼中發現的。

錯誤程式碼:

這種錯誤的例子程式碼量都非常大,我儘可能地選取其中最小的一部分,但是很抱歉,程式碼看起來依舊很冗長。

解釋:

Reason 變數是屬於列舉型別 PhysGunDrop_t,卻用它和屬於另一個列舉型別的常量作比較,這種比較顯然是個邏輯錯誤。

但是這種 bug 模式很普遍,我甚至在像 Clang、TortoiseGit 和 Linux Kernel 這種專案中都有碰到過。

為什麼會如此頻繁?因為在標準C++中,列舉型別本來就不是型別安全的。到底什麼該跟什麼做比較,很容易讓人混淆。

正確程式碼:

我不太確定這段程式碼的正確版本應該是什麼樣的,我猜想 PUNTED_BY_CANNON 應該用 DROPPED_BY_CANNON 或者 LAUNCHED_BY_CANNON 來替代。此處就用 LAUNCHED_BY_CANNON 來代替了。

建議:

如果你是用C++寫程式碼,還沒碰到過這種bug,算你幸運;我強烈建議你從現在開始在程式碼中使用“列舉類”。

對於C++11中的一些新特性,我沒有太多信心。就拿auto關鍵字來說吧,我相信如果頻繁使用的話,會有很多壞處的。 我是這麼看的:比起寫程式碼,程式設計師會花更多的時間閱讀程式碼,所以我們必須確保程式的可讀性很強。 C語言中, 所有變數都必須在函式的一開始就宣告,那麼在函式的中間或者末尾編輯程式碼時,沒那麼容易推測出某個Alice變數到底是什麼意思。這也是為什麼變數的命名規範會這麼多樣化? 例如,字首命名法PfAlice就代表指向浮點數的指標。

C++中你可以隨時隨地宣告變數,這是一種很好的編碼風格。因而使用前字尾命名也不再那麼受歡迎了。接著auto關鍵字出現了,直接導致程式設計師又開始使用各種各樣很難理解的構造形式, 諸如 auto Alice= FOO(); 之類的。 Alice?誰是TM的Alice?(為保留原文字意,此處翻譯稍有不文明)

很抱歉,又偏離我們的主題了。 我想告訴大家的是一些新的特性都有它的好壞兩面性。 但是對於“列舉類”, 我堅信使用它有百利而無一害。

在使用列舉類的時候,必須明確指出指定的常量屬於哪個列舉型別,以免在程式碼中出現錯誤。使用列舉類更新後的程式碼如下:

說真的,修復舊程式碼的確會有一定困難,但我強烈推薦你們現在就開始在程式碼中使用列舉類,你的專案定會從中受益。

我覺得在這兒詳細介紹列舉類沒有多大意義,下面的一些連結可供大家參考學習,從中你會了解到關於C++11這個出色的新特性的所有細節。

  1. Wikipedia. C++11. Strongly typed enumerations.
  2. Cppreference. Enumeration declaration.
  3. StackOverflow. Why is enum class preferred over plain enum?

這個錯誤是用靜態程式碼分析工具 PVS-Studio 檢測到的,錯誤資訊為:V556 對不同列舉型別的值進行比較:Reason == PUNTED_BY_CANNON。

相關文章