首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

C++ 異常處理機制完整教學:從基礎語法到編譯模型深度解析

     編輯:關於C語言

前言

C++ 異常處理是構建健壯軟體的基石。本文提供完整可編譯的示範程式,涵蓋自訂異常類別、多種拋出/捕獲策略、拷貝建構行為、記憶體管理、異常規格與最佳實踐。

一、 完整可執行示範程式

#include "stdafx.h"
#include <stdlib.h>
#include <crtdbg.h>
#include <iostream>

// 記憶體洩漏檢測(Debug 版)
#define _CRTDBG_MAP_ALLOC
#ifdef _DEBUG
#define new new(_NORMAL_BLOCK, __FILE__, __LINE__)
#endif

// 自訂異常類別
class MyException {
public:
    MyException(int errorId) : m_errorId(errorId) {
        std::cout << "MyException ctor called" << std::endl;
    }
    MyException(const MyException& other) : m_errorId(other.m_errorId) {
        std::cout << "Copy ctor called" << std::endl;
    }
    ~MyException() {
        std::cout << "~MyException dtor called" << std::endl;
    }
    int getErrorId() const { return m_errorId; }
private:
    int m_errorId;
};

int main(int argc, char* argv[]) {
    _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);

    int throwErrorCode = 110;
    std::cout << "Input test code: " << std::endl;
    std::cin >> throwErrorCode;

    try {
        if (throwErrorCode == 110) {
            MyException myStru(110);
            throw &myStru;                    // 拋出指標:不觸發拷貝建構
        }
        else if (throwErrorCode == 119) {
            MyException myStru(119);
            throw myStru;                     // 拋出物件:觸發拷貝建構(建立臨時物件)
        }
        else if (throwErrorCode == 120) {
            MyException* pMyStru = new MyException(120);
            throw pMyStru;                    // 拋出 new 物件指標:catch 中需手動 delete
        }
        else {
            throw MyException(throwErrorCode); // 拋出臨時物件:兩次拷貝建構
        }
    }
    catch (MyException* pEx) {
        std::cout << "catch(MyException*) executed" << std::endl;
        std::cout << "Error Code: " << pEx->getErrorId() << std::endl;
        // 若為 new 物件需 delete,但異常物件通常在專用棧上,不需 delete
    }
    catch (MyException ex) {
        std::cout << "catch(MyException) executed" << std::endl;
        std::cout << "Error Code: " << ex.getErrorId() << std::endl;
    }
    catch (...) {
        std::cout << "catch(...) executed" << std::endl;
        throw; // 重新拋出給上層
    }

    int temp; std::cin >> temp; // 暫停
    return 0;
}

二、 執行行為分析(四種拋出方式對比)

拋出方式代碼拷貝建構次數捕獲型別備註
拋出區域物件指標throw &obj;0catch(T*)高效,但 obj 必須生命週期覆蓋 catch(區域變數通常滿足)
拋出區域物件throw obj;1(建立臨時物件)catch(T)catch 再次拷貝建構接收,共 2 次拷貝(C++17 拷貝省略優化)
拋出 new 物件指標throw new T;0catch(T*)不推薦:catch 必須 delete,否則洩漏;異常傳播中易忘記
拋出臨時物件throw T(arg);2(建立臨時→catch 拷貝)catch(T)簡潔但開銷最大

三、 核心知識點深度解析

1. 異常傳播與棧展開

  • throw 後,運行時沿呼叫棧向上尋找匹配的 catch
  • 沿途所有**已建構完成的區域物件**析構函數**自動被呼叫**(棧展開)
  • 若抵達 main 仍無匹配 catch,呼叫 std::terminate() 終止程式,**不保證析構函數執行**

2. 異常物件儲存位置

異常物件**不在函式棧上**,而是在**專用異常棧**上,因此能跨越多層函式傳播而不被棧清理銷毀。

3. 捕獲參數建議:以參考捕獲

catch (const MyException& ex)  // 推薦
catch (MyException* pEx)        // 次之,利用多型
catch (MyException ex)          // 不推薦:額外拷貝、物件切割風險
  • 避免拷貝開銷
  • 支援多型:捕獲基類參考可捕獲派生類異常
  • 派生類 catch 必須置於基類 catch **之前**

4. 異常規格

void func() throw(MyException, std::bad_alloc); // 僅拋出列舉型別(C++11 棄用)
void func() throw();                            // 不拋出任何異常(noexcept 對應)
void func();                                    // 可拋出任何異常
  • 違反規格 → 呼叫 std::unexpected()(預設終止)
  • 現代 C++ 推薦使用 noexcept + 文檔說明

5. 建構函數中拋出異常的資源洩漏陷阱

class ResourceHolder {
    Handle* h1, *h2;
public:
    ResourceHolder() {
        h1 = acquire1();
        if (!h1) throw std::runtime_error("h1 failed");
        h2 = acquire2();
        if (!h2) { release(h1); throw std::runtime_error("h2 failed"); } // 必須手動釋放 h1
    }
    ~ResourceHolder() { release(h1); release(h2); } // 若建構完成會自動呼叫
};

鐵律:建構函數拋出異常前,**必須手動釋放已獲取資源**,因為析構函數**不會**被呼叫。

6. catch(...) 重新拋出

catch (...) {
    log_error();
    throw; // 保持原異常型別、原堆疊資訊重新拋出
}

四、 最佳實踐總結

  1. **異常類別繼承 std::exception**,覆寫 what()
  2. **以 const 參考捕獲**,按型別層級從派生到基底排序 catch
  3. **避免拋出指標/新物件**,優先拋出值型別(臨時物件)
  4. **資源獲取即初始化(RAII)**:智慧指標、容器、鎖包裝器自動管理資源,異常安全
  5. **異常僅用於「異常狀況」**,非預期錯誤(檔案不存在、網路中斷),而非正常流程控制
  6. **邊界轉換層**將 C 錯誤碼/OS 異常轉為 C++ 異常,內部統一異常模型

本文出自 “對影成三人” 博客,請務必保留此出處 https://ticktick.blog.51cto.com/823160/191881

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved