簡體   English   中英

在 C++ 中使用 assert() 是不好的做法嗎?

[英]Is using assert() in C++ bad practice?

我傾向於在我的 C++ 代碼中添加大量斷言,以便在不影響發布版本性能的情況下更容易調試。 現在, assert是一個純 C 宏,設計時沒有考慮 C++ 機制。

另一方面,C++ 定義了std::logic_error ,它意味着在程序邏輯中存在錯誤的情況下拋出(因此得名)。 拋出一個實例可能只是assert的完美的、更 C++ 風格的替代方案。

問題是assertabort都立即終止程序而不調用析構函數,因此跳過清理,而手動拋出異常會增加不必要的運行時成本。 解決這個問題的一種方法是創建一個自己的斷言宏SAFE_ASSERT ,它的工作方式與 C 對應物一樣,但在失敗時拋出異常。

對於這個問題,我能想到三個意見:

  • 堅持 C 的斷言。 由於程序會立即終止,因此更改是否正確展開並不重要。 此外,在 C++ 中使用#define也同樣糟糕。
  • 拋出異常並在 main() 中捕獲它 允許代碼在程序的任何狀態下跳過析構函數都是不好的做法,必須不惜一切代價避免,調用 terminate() 也是如此。 如果拋出異常,則必須捕獲它們。
  • 拋出異常並讓它終止程序。 終止程序的異常是可以的,並且由於NDEBUG ,這在發布版本中永遠不會發生。 捕獲是不必要的,並將內部代碼的實現細節暴露給main()

這個問題有明確的答案嗎? 有專業參考嗎?

編輯:當然,跳過析構函數不是未定義的行為。

  • 斷言用於調試 您所提供代碼的用戶不應看到它們。 如果斷言被命中,你的代碼需要被修復。

    CWE-617:可達斷言

該產品包含可以被攻擊者觸發的 assert() 或類似語句,這會導致應用程序退出或其他比必要更嚴重的行為。

雖然斷言有助於捕獲邏輯錯誤並減少達到更嚴重漏洞情況的機會,但它仍然可能導致拒絕服務。

例如,如果服務器同時處理多個連接,並且在一個連接中發生 assert() 導致所有其他連接被丟棄,則這是導致拒絕服務的可達斷言。

  • 例外是針對特殊情況的 如果遇到,用戶將無法做她想做的事,但可以在其他地方繼續。

  • 錯誤處理適用於正常的程序流程。 例如,如果您提示用戶輸入一個數字並得到一些無法解析的信息,這是正常的,因為用戶輸入不受您的控制,您必須始終按照理所當然地處理所有可能的情況。 (例如循環直到你有一個有效的輸入,中間說“對不起,再試一次”。)

斷言完全適用於 C++ 代碼。 異常和其他錯誤處理機制並不是真正用於與斷言相同的事情。

錯誤處理適用於有可能向用戶很好地恢復或報告錯誤的情況。 例如,如果嘗試讀取輸入文件時出現錯誤,您可能需要對此做一些事情。 錯誤可能由錯誤導致,但它們也可能只是給定輸入的適當輸出。

斷言用於諸如在通常不會檢查 API 時檢查 API 要求是否得到滿足的事情,或者檢查開發人員認為他通過構造保證的事情。 例如,如果算法需要排序輸入,您通常不會檢查它,但您可能有一個斷言來檢查它,以便調試構建標記那種錯誤。 斷言應始終表明程序運行不正確。


如果您正在編寫一個程序,其中不正常關閉可能會導致問題,那么您可能希望避免斷言。 嚴格按照 C++ 語言的未定義行為在這里不屬於此類問題,因為命中斷言可能已經是未定義行為的結果,或者違反了一些其他要求,這可能會阻止某些清理工作正常進行。

此外,如果您根據異常實現斷言,那么它可能會被捕獲並“處理”,即使這與斷言的目的相矛盾。

斷言可用於驗證內部實現不變量,例如某些方法執行之前或之后的內部狀態等。如果斷言失敗,則確實意味着程序邏輯已損壞,您無法從中恢復。 在這種情況下,您能做的最好的事情就是盡快中斷而不將異常傳遞給用戶。 斷言的真正好處(至少在 Linux 上)是核心轉儲是作為進程終止的結果生成的,因此您可以輕松地調查堆棧跟蹤和變量。 這對於理解邏輯失敗比異常消息更有用。

由於 alling abort() 而沒有運行析構函數並不是未定義的行為!

如果是,那么調用std::terminate()也是未定義的行為,那么提供它有什么意義呢?

assert()在 C++ 中和在 C 中一樣有用。斷言不是用於錯誤處理,而是用於立即中止程序。

恕我直言,斷言是為了檢查條件,如果被違反,其他一切都是廢話。 因此,您無法從它們中恢復,或者更確切地說,恢復是無關緊要的。

我將它們分為兩類:

  • 開發人員的罪過(例如,返回負值的概率函數):

浮動概率(){返回-1.0; }

斷言(概率()> = 0.0)

  • 機器壞了(例如,運行您程序的機器非常錯誤):

整數 x = 1;

斷言(x > 0);

這些都是微不足道的例子,但與現實相差不遠。 例如,考慮返回負索引以用於向量的朴素算法。 或定制硬件中的嵌入式程序。 或者更確切地說,因為sh*t 發生了

如果存在此類開發錯誤,您不應該對實施的任何恢復或錯誤處理機制充滿信心。 這同樣適用於硬件錯誤。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM