How to: 設計例外狀況的安全
其中一個例外狀況機制的優點是,執行例外狀況的相關資料,直接從擲回例外狀況的第一個 catch 陳述式處理它的陳述式跳躍。處理常式可能是任意的層級在呼叫堆疊。呼叫在 Try 陳述式和 throw 陳述式之間沒有要求的函式知道關於擲回例外狀況。然而,它們必須被設計,以便超出範圍「意外」隨時例外狀況可能會從下面的地方傳播,而這麼做,而不用忘記部分建立的物件、遺漏在無法使用的狀態的記憶體或資料結構。
基本技術
一項強大例外狀況處理原則需要仔細考量,應該是設計程序的一部分。一般而言,大部分例外狀況偵測並擲回在軟體模組的較低層,不過,這些圖層通常沒有足夠的內容處理錯誤或公開訊息在使用者。在中介層,函式可以攔截並重新擲回例外狀況,則必須檢查例外狀況物件時,或有其他有用的資訊提供最上層最後攔截例外狀況。函式應該攔截,並請忍受的例外狀況時,才可以從它完全復原。在大部分情況下,在中介層的正確行為是讓呼叫堆疊中的例外狀況傳播。在最高的圖層,讓未處理的例外狀況會結束程式可能是適當的例外狀況,則在它的正確性無法保證狀態將程式保留。
不論函式處理例外狀況,協助確保為例外狀況安全,必須根據下列基本規則設計它。
保持資源類別簡單
當您封裝在類別中手動資源管理,請使用做 Nothing 處理每個資源的類別;否則,您可能會造成遺漏。
使用 RAII 慣用語處置資源。
若要為例外狀況安全,函式必須確定物件其配置使用 malloc 或終結 new 和任何資源 (例如檔案控制代碼已關閉或釋放,即使擲回例外狀況。Resource Acquisition Is Initialization,RAII) (RAII) 慣用語 WITH TIES 管理的這類資源至壽命自動變數。發生例外狀況,當函式超出範圍時,透過一般傳回或,所有完整建構的自動變數的解構函式叫用。一 RAII 包裝函式物件 (例如智慧型指標在呼叫其解構函式的適當刪除或終止函式。用無例外狀況之虞的程式碼,直接將每個資源擁有權到 RAII 物件為其最重要的。請注意 vector、 string、 make_shared、 fstream和類似的類別處理序資源的作業中。不過,,因為資源擷取由使用者執行而不是物件, unique_ptr 與傳統 shared_ptr 建構特別的;因此,它們計數,因為 資源版本是解構函式 ,但可疑的做為 RAII。
三個例外狀況確保
通常,例外狀況安全討論根據函式可能提供的三個例外狀況保證: 無期限確保、 強的保證和 基本的保證。
無期限確保
無期限 (或,而非擲回」) 保證函式可能提供的最強的保證。它說明,函式不會擲回例外狀況也不會允許一傳播。不過,您無法可靠地提供這類保證,除非您知道所有虛擬函式這函式呼叫也是無期限或 (B) 您知道擲回的所有例外狀況,並在到達這個函式之前或 (c) 您會攔截並正確處理可能不會到達這個函式的所有例外狀況。
這個功能以確保與基礎確保依賴這個假設解構函式是無失效。所有容器和輸入其解構函式不會擲回的標準程式庫保證。也有相反的需求:標準程式庫會要求將中的使用者定義型別,則為,因為樣板引數必須有非擲回的解構函式。
會以確保
這個功能的保證,說明,如果發生例外狀況,函式超過範圍,則不會遺漏記憶體,而程式狀態不會修改。提供強大的保證的函式基本上是有認可或復原語意的交易:或者完全成功或它沒有作用。
基礎確保
這個基本的保證最弱式三。不過,當,強大的確保太昂貴的在記憶體用量或在效能時,它可能是最佳選擇。這個基本的保證,說明,如果發生例外狀況,記憶體不會遺漏,而且物件仍處於可用的狀態,即使可能已經修改了資料。
例外狀況安全類別
類別可協助確保它的例外狀況,安全,即使它是不安全的函式使用,藉由防止部分建構或部分被終結。如果類別建構函式在完成之前結束,則物件會建立,而且其解構函式不會呼叫。雖然在例外狀況之前初始化的自動變數都會叫用解構函式,以動態方式不為智慧型指標或類似的自動變數處理配置記憶體或資源將遺漏。
內建型別是所有無失效,因此,標準程式庫型別支援這個基礎確保在最小。遵循一定是例外狀況安全的使用者定義型別的方針:
使用智慧型指標或其他 RAII 型別的包裝函式來處理所有資源。避免在類別解構函式的資源管理功能,,因為解構函式不會叫用,如果建構函式擲回例外狀況。不過,此類別,則是控制資源的專屬的資源管理員,然後使用解構函式處理資源是可接受的。
了解基底類別建構函式所擲回的例外狀況會在衍生類別建構函式不可以忍受。如果您要轉譯和重新擲回在衍生的類別建構函式的基底類別例外狀況,請使用函式 try 區塊。如需詳細資訊,請參閱How to: 處理例外狀況的基底類別建構函式 (C++)。
考慮是否儲存所有類別狀態在智慧型指標包裝的資料成員,,特別是如果類別不允許失敗「初始化的概念。雖然 C++ 允許未初始化的資料成員,所以不支援未初始化或部分初始化的類別執行個體。建構函式必須成功或失敗;,如果建構函式不會執行到完成,物件不會。
不允許任何例外狀況從解構函式逸出。C++ 基底公理是解構函式不應該允許例外狀況散佈到呼叫堆疊。如果解構函式必須執行可能會擲回例外狀況的作業,它在 try catch 區塊必須如此做和忍受例外狀況。標準程式庫在任何解構函式所定義的這個保證。