【C++】noexcept的作用
noexcept是C++11引入的關鍵字,用于指定函數是否會拋出異常。它既是一個修飾符也是一個操作符,在現代C++編程中扮演著重要角色。
一、noexcept的基本概念
noexcept主要有兩種形式:
- 無條件形式?:void func() noexcept; 表示函數func保證不會拋出任何異常
- 條件形式?:void func() noexcept(expression); 根據括號內的表達式結果決定是否拋出異常。如果表達式為true,表示不拋出異常;為false則表示可能拋出異常。
不帶條件的noexcept等同于noexcept(true)。當標記為noexcept的函數確實拋出了異常,程序會立即調用std::terminate()終止運行,不會保證調用對象的析構函數。
二、noexcept的主要作用
1. 性能優化
編譯器可以利用noexcept信息進行多種優化:
- 避免生成額外的異常處理代碼,減少代碼大小
- 優化函數調用棧的管理,不需要為可能的異常保留額外空間
- 標準庫容器(如std::vector)會根據元素類型的移動操作是否為noexcept來決定使用移動還是拷貝操作
2. 代碼可靠性與安全性
- 明確表明函數不會拋出異常,有助于代碼審查和靜態分析
- 減少程序崩潰風險,因為如果noexcept函數拋出異常,程序會立即終止
- 阻止異常的傳播與擴散,避免異常在調用棧中向上傳遞
3. 影響函數重載和模板特化
noexcept可以作為函數類型的一部分,影響函數重載決策和模板特化:
void foo() noexcept;
void foo() noexcept(false);
編譯器可能優先選擇noexcept版本
三、noexcept的典型使用場景
1. 移動構造函數和移動賦值運算符?
標準庫容器在重新分配內存時,如果移動操作是noexcept,會優先使用移動而非拷貝.
2. 析構函數?
析構函數通常不應拋出異常,默認就是noexcept的,除非顯式聲明為noexcept(false)
3.交換(swap)函數?
交換操作通常不應拋出異常,可以標記為noexcept
4.性能關鍵的函數?
在性能敏感的代碼中,標記為noexcept可以幫助編譯器優化
5.遞歸函數?
遞歸函數聲明為noexcept可以避免多次調用時的異常處理開銷
四、noexcept操作符
noexcept還可以作為操作符使用,返回一個布爾值,表示表達式是否會拋出異常:
noexcept(expr) // 如果expr不會拋出異常則返回true,否則返回false
這在模板編程中特別有用,可以根據類型特性動態決定函數是否為noexcept。
五、注意事項
?1. 謹慎使用?
錯誤標記noexcept可能導致程序在異常情況下直接終止,難以調試
?2. 異常安全?
即使函數標記為noexcept,也應確保其實現是異常安全的,使用RAII技術管理資源
3. ?不要過度承諾?
只在確定函數不會拋出異常時才使用noexcept