通過 const 引用按值捕獲的最佳實踐

一般來說,按值(而不是通過指標)丟擲是一種好的做法,但是通過(const)引用來捕獲。

try {
    // throw new std::runtime_error("Error!");   // Don't do this!
    // This creates an exception object
    // on the heap and would require you to catch the
    // pointer and manage the memory yourself. This can
    // cause memory leaks!
    
    throw std::runtime_error("Error!");
} catch (const std::runtime_error& e) {
    std::cout << e.what() << std::endl;
}

通過引用捕獲是一個好的做法的一個原因是它消除了在傳遞給 catch 塊時(或當傳播到其他 catch 塊時)重構物件的需要。通過引用捕獲還允許以多型方式處理異常並避免物件切片。但是,如果要重新丟擲異常(如 throw e;,請參見下面的示例),你仍然可以獲得物件切片,因為 throw e; 語句會根據宣告的型別生成異常的副本:

#include <iostream>

struct BaseException {
    virtual const char* what() const { return "BaseException"; }
};

struct DerivedException : BaseException {
    // "virtual" keyword is optional here
    virtual const char* what() const { return "DerivedException"; }
};

int main(int argc, char** argv) {
    try {
        try {
            throw DerivedException();
        } catch (const BaseException& e) {
            std::cout << "First catch block: " << e.what() << std::endl;
            // Output ==> First catch block: DerivedException

            throw e; // This changes the exception to BaseException
                     // instead of the original DerivedException!
        }
    } catch (const BaseException& e) {
        std::cout << "Second catch block: " << e.what() << std::endl;
        // Output ==> Second catch block: BaseException
    }
    return 0;
}

如果你確定不打算更改異常(例如新增資訊或修改訊息),那麼使用 const 引用可以使編譯器進行優化並提高效能。但這仍然會導致物件拼接(如上例所示)。

警告: 小心在 catch 塊中丟擲意外異常,尤其是與分配額外記憶體或資源有關。例如,構造 logic_errorruntime_error 或它們的子類可能會因複製異常字串時記憶體耗盡而丟失 bad_alloc,I / O 流可能會在日誌記錄期間丟擲相應的異常掩碼集等。