最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

C++異常機(jī)制:拋出、捕獲與棧展開

 更新時(shí)間:2026年05月21日 10:58:43   作者:無限進(jìn)步_  
這篇文章把異常的拋出、捕獲、棧展開過程以及類型匹配規(guī)則梳理一遍,最后用一組自定義異常體系展示實(shí)際項(xiàng)目中怎么用,結(jié)合實(shí)例代碼給大家介紹的非常詳細(xì),感興趣的朋友跟隨小編一起看看吧

C++異常機(jī)制通過 throw(拋出)、try/catch(捕獲)和棧展開(Stack Unwinding)實(shí)現(xiàn)結(jié)構(gòu)化錯(cuò)誤處理,核心是“檢測與分離”+“自動(dòng)資源清理”。‌‌‌

  • ‌拋出(throw)‌:在檢測到錯(cuò)誤時(shí)用 throw 表達(dá)式; 拋出一個(gè)異常對象(可為內(nèi)置類型、字符串或類對象,推薦繼承 std::exception);拋出后立即跳轉(zhuǎn),后續(xù)代碼不執(zhí)行,并生成異常對象的拷貝用于傳播。
  • ‌捕獲(try/catch)‌:try 塊包裹可能異常的代碼,后續(xù)接一個(gè)或多個(gè) catch(類型 參數(shù)) 處理匹配異常;‌類型需精確匹配或允許隱式轉(zhuǎn)換‌(如 const 權(quán)限縮小、派生類→基類引用/值);catch(...) 為萬能兜底,但無法獲取異常詳情;‌匹配的是離拋出點(diǎn)最近的、類型兼容的 catch‌。
  • ‌棧展開(Stack Unwinding)‌:若當(dāng)前函數(shù)無匹配 catch,‌自動(dòng)逆序析構(gòu)當(dāng)前函數(shù)所有已構(gòu)造的局部對象‌,逐層返回調(diào)用棧,直至找到匹配 catch 或抵達(dá) main 未捕獲則調(diào)用 std::terminate() 終止程序;此過程確保 RAII 資源(如鎖、內(nèi)存)正確釋放,‌但析構(gòu)函數(shù)自身不得拋異常‌(否則立即 terminate)。‌‌

異常傳播中,‌throw;(無表達(dá)式)可在 catch 內(nèi)重新拋出原異常‌(保持原始類型);C++11 起棄用 throw() 規(guī)格說明,改用 noexcept 聲明“不拋異常”,違反則直接 terminate。合理使用可分離錯(cuò)誤邏輯與主流程,但濫用影響性能與可讀性。‌‌

錯(cuò)誤處理是程序里繞不開的話題。C語言用錯(cuò)誤碼,函數(shù)返回一個(gè)int,調(diào)用方去查表,麻煩不說,還容易把業(yè)務(wù)邏輯和錯(cuò)誤處理攪在一起。C++的異常機(jī)制提供了一種不同的思路:把發(fā)現(xiàn)錯(cuò)誤處理錯(cuò)誤的代碼分開。出錯(cuò)的模塊只負(fù)責(zé)拋出異常,至于怎么處理,由調(diào)用鏈上合適的捕獲點(diǎn)決定,兩邊互不侵入。

這篇文章把異常的拋出、捕獲、棧展開過程以及類型匹配規(guī)則梳理一遍,最后用一組自定義異常體系展示實(shí)際項(xiàng)目中怎么用。

1. 拋出與捕獲的基本姿勢

1.1 throw干了什么

當(dāng)程序運(yùn)行到throw語句時(shí),會構(gòu)造一個(gè)異常對象,然后函數(shù)的剩余代碼不再執(zhí)行,控制權(quán)開始沿著調(diào)用鏈向上轉(zhuǎn)移,尋找匹配的catch。這個(gè)過程有三個(gè)關(guān)鍵點(diǎn):

  • throw后面的語句被跳過。
  • 當(dāng)前函數(shù)可能提前退出。
  • 局部對象會按順序析構(gòu)(棧展開時(shí)完成)。

throw的異常對象如果是局部的,會生成一份拷貝交給catch,原對象在棧展開過程中銷毀。這份拷貝會在catch處理完畢后銷毀。

1.2 try/catch怎么接

void Func() {
    int len, time;
    cin >> len >> time;
    try {
        cout << Divide(len, time) << endl;
    } catch (const char* errmsg) {
        cout << errmsg << endl;
    }
    cout << "Func continuing..." << endl;
}
double Divide(int a, int b) {
    if (b == 0) {
        string s("Divide by zero condition!");
        throw s;
    }
    return (double)a / (double)b;
}

Divide里拋出的是string對象,而Func里捕獲的類型是const char*,不匹配。所以Func的catch會被跳過,繼續(xù)向外層調(diào)用者(這里是main)尋找匹配的catch(string)。如果main也找不到,程序就調(diào)用terminate終止。

catch的匹配規(guī)則

  • 一般情況下要求類型完全匹配。
  • 允許從非常量向常量的轉(zhuǎn)換(權(quán)限縮小方向),但不允許其他隱式類型轉(zhuǎn)換(比如int轉(zhuǎn)double)。
  • 數(shù)組會被轉(zhuǎn)換為指向元素類型的指針,函數(shù)轉(zhuǎn)換為函數(shù)指針。
  • 支持派生類向基類的轉(zhuǎn)換。這個(gè)在實(shí)際項(xiàng)目中非常實(shí)用,后面會展開。

如果外層沒有匹配的catch,main函數(shù)最后通常會寫一個(gè)catch(...)兜底,防止程序崩潰,但它只能捕獲,沒法知道具體錯(cuò)誤信息。

2. 棧展開:異常的傳播路徑

棧展開(stack unwinding)是理解異常行為的關(guān)鍵。從throw開始,編譯器會依次查找:

  • 檢查throw本身是否在try塊內(nèi)。如果在,看當(dāng)前try塊的catch有沒有匹配的。
  • 有匹配就跳到catch執(zhí)行,之后從這個(gè)catch結(jié)束后的第一條語句繼續(xù)運(yùn)行。
  • 沒有匹配,則退出當(dāng)前函數(shù),析構(gòu)所有局部對象,回到調(diào)用方函數(shù)的調(diào)用點(diǎn),重復(fù)第一步。
  • 到達(dá)main還沒匹配,調(diào)用terminate終止程序。

假設(shè)調(diào)用鏈?zhǔn)牵?code>main() → func3() → func2() → func1(),func1拋出異常,catch在main里。它會依次退出func1、func2、func3的棧幀,直到在main里找到匹配的catch。這里“退出”不是簡單的跳轉(zhuǎn),而是沿著調(diào)用鏈,層層析構(gòu)局部對象,這保證了RAII資源能被正確釋放。

3. 自定義異常體系:利用派生類到基類的轉(zhuǎn)換

在實(shí)際項(xiàng)目中,通常不會到處拋string或基本類型,而是構(gòu)建一個(gè)異常類體系,基類可以是std::exception或者自己寫的類,各模塊派生自己的異常類型。捕獲時(shí)只捕獲基類引用,就能統(tǒng)一處理所有異常。

// 基類
class Exception {
public:
    Exception(const string& errmsg, int id)
        : _errmsg(errmsg), _id(id) {}
    virtual string what() const { return _errmsg; }
    int getid() const { return _id; }
protected:
    string _errmsg;
    int _id;
};
// 各模塊派生自己的異常
class SqlException : public Exception {
public:
    SqlException(const string& errmsg, int id, const string& sql)
        : Exception(errmsg, id), _sql(sql) {}
    string what() const override {
        return "SqlException:" + _errmsg + "->" + _sql;
    }
private:
    string _sql;
};
class CacheException : public Exception {
public:
    CacheException(const string& errmsg, int id)
        : Exception(errmsg, id) {}
    string what() const override {
        return "CacheException:" + _errmsg;
    }
};
class HttpException : public Exception {
public:
    HttpException(const string& errmsg, int id, const string& type)
        : Exception(errmsg, id), _type(type) {}
    string what() const override {
        return "HttpException:" + _type + ":" + _errmsg;
    }
private:
    string _type;
};

業(yè)務(wù)函數(shù)模擬拋出各類異常:

void SQLMgr() {
    if (rand() % 7 == 0)
        throw SqlException("權(quán)限不足", 100, "select * from name = '張三'");
    cout << "SQLMgr 調(diào)用成功" << endl;
}
void CacheMgr() {
    if (rand() % 5 == 0)
        throw CacheException("權(quán)限不足", 100);
    else if (rand() % 6 == 0)
        throw CacheException("數(shù)據(jù)不存在", 101);
    cout << "CacheMgr 調(diào)用成功" << endl;
    SQLMgr();
}
void HttpServer() {
    if (rand() % 3 == 0)
        throw HttpException("請求資源不存在", 100, "get");
    else if (rand() % 4 == 0)
        throw HttpException("權(quán)限不足", 101, "post");
    cout << "HttpServer調(diào)用成功" << endl;
    CacheMgr();
}

主函數(shù)里只需要捕獲基類引用:

int main() {
    srand(time(0));
    while (1) {
        this_thread::sleep_for(chrono::seconds(1));
        try {
            HttpServer();
        } catch (const Exception& e) {
            cout << e.what() << endl;
        } catch (...) {
            cout << "Unknown Exception" << endl;
        }
    }
}

捕獲基類引用配合虛函數(shù)what(),既能拿到具體類型的信息,又不需要寫一堆分支類型的catch。新增一個(gè)派生類異常,只要繼承Exception并重寫what(),上層代碼一行不改。這就是開放-封閉原則在錯(cuò)誤處理中的體現(xiàn)。

這里有一個(gè)細(xì)節(jié):catch的參數(shù)應(yīng)該用引用,否則會發(fā)生拷貝切片,派生類的額外信息就丟了。

小結(jié)

異常機(jī)制的本質(zhì)是把錯(cuò)誤檢測和處理解耦,代價(jià)是引入了棧展開帶來的控制流復(fù)雜性和資源管理風(fēng)險(xiǎn)。下一篇文章會順著這個(gè)思路往下走:異常重新拋出、異常安全問題、以及C++11引入的noexcept規(guī)范——它們試圖解決“異常本身帶來的問題”。

到此這篇關(guān)于C++異常機(jī)制:拋出、捕獲與棧展開的文章就介紹到這了,更多相關(guān)C++異常拋出、捕獲與棧展開內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • C++求階乘的兩種方法

    C++求階乘的兩種方法

    這篇文章主要介紹了C++求階乘的兩種方法,有需要的朋友可以參考一下
    2013-12-12
  • C語言詳解關(guān)鍵字sizeof與unsigned及signed的用法

    C語言詳解關(guān)鍵字sizeof與unsigned及signed的用法

    這篇文章主要為大家詳細(xì)介紹了C語言關(guān)鍵字sizeof&&unsigned&&signed,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-06-06
  • stl常用算法(Algorithms)介紹(stl排序算法、非變序型隊(duì)列)

    stl常用算法(Algorithms)介紹(stl排序算法、非變序型隊(duì)列)

    這篇文章主要介紹了stl常用算法(Algorithms)介紹(stl排序算法、非變序型隊(duì)列),需要的朋友可以參考下
    2014-05-05
  • C++文件流操作方式

    C++文件流操作方式

    文章介紹了C++中的IO流體系,與C語言文件操作的對比,文中詳細(xì)描述了流體系的層次、常用流類對象、輸入輸出控制、文件流操作等并,并并詳細(xì)對比了C++IO流與C語言文件操作的區(qū)別,最終總結(jié)了C++IO流體系的設(shè)計(jì)優(yōu)勢與使用要點(diǎn)
    2026-05-05
  • 從使用角度解讀c++20 協(xié)程示例

    從使用角度解讀c++20 協(xié)程示例

    類比線程,線程是個(gè)函數(shù),把這個(gè)函數(shù)交給 創(chuàng)建線程的api,然后這個(gè)函數(shù)就變成線程了,這個(gè)函數(shù)本身沒有任何特殊的地方,就是普通函數(shù),這篇文章主要介紹了從使用角度解讀c++20 協(xié)程示例,需要的朋友可以參考下
    2023-01-01
  • C語言大作業(yè)之圖書管理系統(tǒng)的實(shí)現(xiàn)詳程

    C語言大作業(yè)之圖書管理系統(tǒng)的實(shí)現(xiàn)詳程

    隨著網(wǎng)絡(luò)技術(shù)的高速發(fā)展,計(jì)算機(jī)應(yīng)用的普及,利用計(jì)算機(jī)對圖書館的日常工作進(jìn)行管理勢在必行,趁著寒假時(shí)間手把手帶你用C語言實(shí)現(xiàn)一個(gè)圖書管理系統(tǒng),大家可以在過程中查缺補(bǔ)漏,提升水平
    2022-01-01
  • OpenCV提取圖像中圓線上的數(shù)據(jù)具體流程

    OpenCV提取圖像中圓線上的數(shù)據(jù)具體流程

    在對圖像進(jìn)行處理時(shí),經(jīng)常會要提取出圖像中某條直線、圓線或者ROI區(qū)域內(nèi)的感興趣數(shù)據(jù),進(jìn)行重點(diǎn)關(guān)注。本文主要介紹了利用OpenCV獲取圖像中圓線上的數(shù)據(jù),需要的可以參考一下
    2021-11-11
  • cocos2dx實(shí)現(xiàn)刮獎(jiǎng)效果

    cocos2dx實(shí)現(xiàn)刮獎(jiǎng)效果

    這篇文章主要為大家詳細(xì)介紹了cocos2dx實(shí)現(xiàn)刮獎(jiǎng)效果,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-12-12
  • c++中引用作為形參的使用方法以及作用

    c++中引用作為形參的使用方法以及作用

    這篇文章主要給大家介紹了關(guān)于c++中引用作為形參的使用方法以及作用的相關(guān)資料,引用是地址傳值,作為引用的形參數(shù)值被修改的同時(shí),也修改了對應(yīng)實(shí)參的值,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2023-11-11
  • C語言詳解結(jié)構(gòu)體的內(nèi)存對齊與大小計(jì)算

    C語言詳解結(jié)構(gòu)體的內(nèi)存對齊與大小計(jì)算

    C 數(shù)組允許定義可存儲相同類型數(shù)據(jù)項(xiàng)的變量,結(jié)構(gòu)是 C 編程中另一種用戶自定義的可用的數(shù)據(jù)類型,它允許你存儲不同類型的數(shù)據(jù)項(xiàng),本篇讓我們來了解C 的結(jié)構(gòu)體內(nèi)存對齊與計(jì)算大小
    2022-04-04

最新評論

乐昌市| 伊通| 凌源市| 英吉沙县| 江华| 黎城县| 伊金霍洛旗| 图片| 波密县| 凤翔县| 比如县| 仁化县| 蒙城县| 沁源县| 东兴市| 萝北县| 尼木县| 寿宁县| 晋江市| 宁强县| 贵阳市| 建瓯市| 井陉县| 红安县| 博野县| 巫山县| 云林县| 荔浦县| 吴桥县| 内江市| 平山县| 芜湖县| 苗栗市| 剑阁县| 高雄市| 册亨县| 盘锦市| 武陟县| 丹棱县| 富宁县| 融水|