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

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

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

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

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

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

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

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

1. 拋出與捕獲的基本姿勢(shì)

1.1 throw干了什么

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

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

throw的異常對(duì)象如果是局部的,會(huì)生成一份拷貝交給catch,原對(duì)象在棧展開(kāi)過(guò)程中銷(xiāo)毀。這份拷貝會(huì)在catch處理完畢后銷(xiāo)毀。

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對(duì)象,而Func里捕獲的類(lèi)型是const char*,不匹配。所以Func的catch會(huì)被跳過(guò),繼續(xù)向外層調(diào)用者(這里是main)尋找匹配的catch(string)。如果main也找不到,程序就調(diào)用terminate終止。

catch的匹配規(guī)則

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

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

2. 棧展開(kāi):異常的傳播路徑

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

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

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

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

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

// 基類(lèi)
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ù)模擬拋出各類(lèi)異常:

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("請(qǐng)求資源不存在", 100, "get");
    else if (rand() % 4 == 0)
        throw HttpException("權(quán)限不足", 101, "post");
    cout << "HttpServer調(diào)用成功" << endl;
    CacheMgr();
}

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

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;
        }
    }
}

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

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

小結(jié)

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

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

相關(guān)文章

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

    C++求階乘的兩種方法

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

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

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

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

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

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

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

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

    在對(duì)圖像進(jìn)行處理時(shí),經(jīng)常會(huì)要提取出圖像中某條直線、圓線或者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í),也修改了對(duì)應(yīng)實(shí)參的值,文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2023-11-11
  • C語(yǔ)言詳解結(jié)構(gòu)體的內(nèi)存對(duì)齊與大小計(jì)算

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

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

最新評(píng)論

夏津县| 沾益县| 长垣县| 山东| 东台市| 桦川县| 樟树市| 巍山| 青川县| 乌拉特前旗| 大同县| 丁青县| 汝南县| 永安市| 杭锦后旗| 广西| 开阳县| 夏河县| 江永县| 青阳县| 章丘市| 成都市| 延津县| 寻甸| 湖北省| 昭平县| 壶关县| 苏尼特左旗| 藁城市| 泰宁县| 灌云县| 泸州市| 类乌齐县| 灵川县| 江川县| 商南县| 夏河县| 淳化县| 隆回县| 珲春市| 上林县|