色哟哟视频在线观看-色哟哟视频在线-色哟哟欧美15最新在线-色哟哟免费在线观看-国产l精品国产亚洲区在线观看-国产l精品国产亚洲区久久

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

C++異常機制解析

科技綠洲 ? 來源:Linux開發架構之路 ? 作者:Linux開發架構之路 ? 2023-11-09 14:16 ? 次閱讀

傳統排錯

我們早在 C 程序里面傳統的錯誤處理手段有:

  1. 終止程序,如 assert;缺陷是用戶難以接受,說白了就是一種及其粗暴的手法,比如發生內存錯誤,除0錯誤時就會終止程序。
  2. 返回錯誤碼。缺陷是需要我們自己去查找錯誤,如系統的很多庫的接口函數都是通過把錯誤碼放到 errno 中,表示錯誤。
  3. C標準庫中 setjmp 和 longjmp 組合(不常用)

實際中 C 語言基本都是使用返回錯誤碼的方式處理錯誤,部分情況下使用終止程序處理非常嚴重緊急的錯誤,因此異常機制就時運而橫空出世

概念

異常是面向對象語言常用的一種處理錯誤的方式,當一個函數發現自己無法處理的錯誤時就可以拋出異常,讓函數直接或間接調用者自己來處理這個錯誤

  1. throw:當程序出現問題時,可以通過 throw 關鍵字拋出一個異常
  2. try:try 塊中放置的是可能拋出異常的代碼,該代碼塊在執行時將進行異常錯誤檢測,try 塊后面通常跟著一個或多個 catch 塊。
  3. catch:如果try塊中發生錯誤,則可以在 catch 塊中定義對應要執行的代碼塊。

try-catch 語句的語法實例:

try
{
//被保護的代碼
}
catch (ExceptionName e1)
{
//catch塊
}
catch (ExceptionName e2)
{
//catch塊
}
catch (ExceptionName eN)
{
//catch塊
}

用法

異常是通過拋出對象而引發的,該對象的類型決定了應該激活哪個 catch 的處理代碼,如果拋出的異常對象沒有捕獲,或是沒有匹配類型的捕獲,那么程序會終止報錯

異常捕獲和拋出

被選中的處理代碼(catch塊)是調用鏈中與該對象類型匹配且離拋出異常位置最近的那一個

拋出異常對象后,會生成一個異常對象的拷貝,因為拋出的異常對象可能是一個臨時對象,所以會生成一個拷貝對象 color{red} {因為拋出的異常對象可能是一個臨時對象,所以會生成一個拷貝對象}因為拋出的異常對象可能是一個臨時對象,所以會生成一個拷貝對象,這個拷貝的臨時對象會在被 catch 以后銷毀(類似于函數的傳值返回)

catch(…) 可以捕獲任意類型的異常,但捕獲后無法知道異常錯誤是什么,實際異常拋出和捕獲的匹配原則有個例外,捕獲和拋出的異常類型并不一定要完全匹配,可以拋出派生類對象,使用基類進行捕獲,這個在實際中非常有用

在函數調用鏈中異常棧展開的匹配原則:

當異常被拋出后,首先檢查 throw 本身是否在 try 塊內部,如果在則查找匹配的 catch 語句,如果有匹配的就跳到 catch 的地方進行處理

如果當前沒有匹配的 catch 則退出當前函數棧,繼續在上一個調用中進行查找 catch。找到匹配的 catch 子句并處理以后,會沿著 catch 子句后面繼續執行,而不會跳回到原來拋異常的地方,如果到達 main 函數的棧,依舊沒有找到匹配的 catch 則終止程序

比如下面的代碼中調用了 func3,func3 中調用 func2,func2 中調用 func1,func1 中拋出了一個 string 的異常對象:

void func1()
{
throw string("這是一個異常");
}
void func2()
{
func1();
}
void func3()
{
func2();
}
int main()
{
try
{
func3();
}
catch (const string& s)
{
cout << "錯誤描述:" << s << endl;
}
catch (...)
{
cout << "未知異常" << endl;
}
return 0;
}

首先會檢查 throw 本身是否在 try 塊內部,這里就會因此退出 func1 所在的函數棧,繼續在上一個調用棧中進行查找,即 func2 所在的函數棧,由于 func2 中也沒有匹配的 catch,因此會繼續復讀套娃,最終在 main 函數棧中找到匹配的 catch

這時就會跳到 main 函數中對應的 catch 塊中執行對應的代碼塊,執行完后繼續執行該代碼塊后續的代碼:

圖片

當然為了防止還有漏網之魚,一般此時我們還會搞一個 catch(…) 進行全捕獲。

異常的重新拋出

要知道一個 catch 是無法完全搞定異常的,如果我們對異常進行修正后,希望交付給上層調用鏈進行異常的異常信息日志記錄,此時就需要我們重新對上層函數拋異常:

void func1()
{
throw string("這是一個異常");
}
void func2()
{
int* array = new int[10];
func1();

//省略函數對應實現
//……
delete[] array;
}
int main()
{
try
{
func2();
}
catch (const string& s)
{
cout << s << endl;
}
catch (...)
{
cout << "未知異常" << endl;
}
return 0;
}

這里 func2 最后應該 delete 進行空間釋放,但由于 func2 中途調用 func1 ,func1 內部拋出了一個異常,這時會直接跳轉到 main 函數中的 catch 塊執行對應的異常處理程序,并且在處理完后繼續沿著 catch 塊往后執行,這時就導致 func2 中內存塊沒有得到釋放,造成了內存泄露!

此時我們應該在 func2 中先對 func1 拋出的異常進行捕獲,捕獲后先將內存釋放再重新拋出異常,就可以避免內存泄露:

void func2()
{
int* array = new int[10];
try
{
func1();
//省略函數對應實現
//……
}
catch (...)
{
delete[] array;
throw; //將捕獲到的異常再次重新拋出
}
delete[] array;
}

try-catch 中 new 和 delete 之間可能還會拋出其他類型的異常,因此在 fun2 中最好再進行 catch(…) ,將申請到的內存 delete 后再通過throw 重新拋出;重新拋出異常對象時,此時 throw 可以不用指明要拋出的異常對象,其實 catch(…) 也不知道自己到底捕了個什么異常對象

安全第一條

還是那句話,道路千萬條,拋異常要謹慎:

  1. 構造函數完成對象的構造和初始化,最好不要在構造函數中拋出異常,否則可能導致對象不完整或沒有完全初始化
  2. 析構函數完成對象資源的清理,最好不要在析構函數中拋出異常,否則可能導致內存泄露,句柄未關閉等
  3. C++ 中在 new 和 delete 中拋出異常經常是內存泄漏的罪魁禍首,在 lock 和 unlock 之間拋出異常導致死鎖,C++ 經常使用 RAII 的方式來解決類似問題

規范使用

站在異常的嚴謹立場上, C++ 也在盡量提高咱的使用規范:

在函數的后面接throw(type1, type2, …),列出這個函數可能拋擲的所有異常類型
在函數的后面接throw()或noexcept(C++11),表示該函數不拋異常
若無異常接口聲明,則此函數可以拋擲任何類型的異常(異常接口聲明不是強制的)

//這里可能會拋出A/B/C/D類型的異常
void func() throw(A, B, C, D);
//這里只會拋出 bad_alloc 的異常
void* operator new(std::size_t size) throw(std::bad_alloc);
//這里不會拋出異常
void* operator new(std::size_t size, void* ptr) throw();

異常體系

因為異常屬實需要嚴謹與規范的操作,所以在很多公司里面都會制定自己的一套異常的規范管理:

公司中的項目一般會進行模塊劃分,讓不同的人或小組完成不同的模塊,如果不對拋異常這件事進行規范,那么在最外層捕獲異常的冤種就會問候親媽了,因為他會來給各位擦屁股,捕獲大家拋出的所以異常對象 color{red} {那么在最外層捕獲異常的冤種就會問候親媽了,因為他會來給各位擦屁股,捕獲大家拋出的所以異常對象}那么在最外層捕獲異常的冤種就會問候親媽了,因為他會來給各位擦屁股,捕獲大家拋出的所以異常對象

我們之前說過異常語法可以用基類捕獲拋出的派生類對象,因此實際中都會先定義一個最基礎的異常類,所有人拋出的異常對象都必須是繼承于該異常類的派生類對象,,因此最外層就只需捕獲基類就行了

圖片

最基礎的異常類至少需要包含錯誤編號和錯誤描述兩個成員變量,甚至還可以包含當前函數棧幀的調用鏈等信息,該異常類中一般還會提供兩個成員函數,分別用來獲取錯誤編號和錯誤描述

class Exception
{
public:
Exception(int errid, const char* errmsg)
:_errid(errid)
, _errmsg(errmsg)
{}
int GetErrid() const
{
return _errid;
}
virtual string what() const
{
return _errmsg;
}
protected:
int _errid; //錯誤編號
string _errmsg; //錯誤描述
//...
};

其他人如果要對這個異常類進行擴展,必須先繼承基礎異常類,然后按需添加某些成員變量,或是對虛函數what 進行重寫,使其能告知更多的異常信息:

class CacheException : public Exception
{
public:
CacheException(int errid, const char* errmsg)
:Exception(errid, errmsg)
{}
virtual string what() const
{
string msg = "CacheException: ";
msg += _errmsg;
return msg;
}
protected:
//...
};
class SqlException : public Exception
{
public:
SqlException(int errid, const char* errmsg, const char* sql)
:Exception(errid, errmsg)
, _sql(sql)
{}
virtual string what() const
{
string msg = "CacheException: ";
msg += _errmsg;
msg += "sql語句: ";
msg += _sql;
return msg;
}
protected:
string _sql; //異常的SQL語句
//...
};

異常類的成員變量不能設置為私有,因為私有成員在子類中是不可見的。基類 Exception 中 what 成員函數最好定義為虛函數,方便子類對其進行重寫,從而達到多態的效果

標準庫體系

C++ 標準庫當中的異常也是一個基礎體系,其中 exception 就是基類,它與其他異常類的繼承關系如下:

圖片

其中具體信息如下:

圖片

我們可以去繼承這里的 exception 類來實現自己的異常類,但實際上很多公司都會自己定義一套異常繼承體系!

優缺點

目前情況來看,異常是利大于弊的,還是鼓勵使用異常的,而且前排的語言基本都會使用異常處理錯誤,這也可以看出這是大勢所趨

異常的優點:

相比錯誤碼,異??梢郧逦鷾蚀_的展示出錯誤的各種信息,甚至可以包含堆棧調用等信息,這樣可以幫助更好的定位程序的bug

返回錯誤碼的傳統方式有個很大的問題就是,在函數調用鏈中,深層的函數返回了錯誤,那么我們得層層返回錯誤碼,最終最外層才能拿到錯誤

很多的第三方庫都會使用異常,比如 boost、gtest、gmock 等常用的庫,如果我們不用異常就不能很好的發揮這些庫的作用,很多測試框架也都使用異常,因此使用異常能更好的使用單元測試等進行白盒測試

部分函數使用異常更好處理,比如 T& operator 這樣的函數,如果 pos 越界了只能使用異常或者終止程序處理,沒辦法通過返回值表示錯誤

異常的缺點:

異常會導致程序的執行流混亂,這會導致我們跟蹤調試或分析程序時比較困難。異常還會有一些性能的開銷,當然在現代硬件速度很快的情況下,這個影響基本忽略不計!

C++ 沒有垃圾回收機制,資源需要自己管理,有了異常非常容易導致內存泄露、死鎖等異常安全問題,這個需要使用 RAII 來處理資源的管理問題,學習成本比較高

C++ 標準庫的異常體系定義得不夠好,導致大家各自定義自己的異常體系,非常的混亂,異常盡量規范使用,否則后果不堪設想,隨意拋異常,也會讓外層捕獲的用戶苦不堪言。

異常接口聲明不是強制的,對于沒有聲明異常類型的函數,無法預知該函數是否會拋出異常

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • 模塊
    +關注

    關注

    7

    文章

    2731

    瀏覽量

    47661
  • 程序
    +關注

    關注

    117

    文章

    3795

    瀏覽量

    81299
  • 函數
    +關注

    關注

    3

    文章

    4345

    瀏覽量

    62875
  • C++
    C++
    +關注

    關注

    22

    文章

    2114

    瀏覽量

    73792
收藏 人收藏

    評論

    相關推薦

    C++避坑指南

    C++是一門古老的語言,但仍然在不間斷更新中,不斷引用新特性。但與此同時 C++又甩不掉巨大的歷史包袱,并且 C++的設計初衷和理念造成了 C++
    發表于 09-15 09:04 ?615次閱讀

    關于C++中的函數重載機制

    函數重載是C++的新增機制,是在同一個作用域中能聲明定義多個同名字的函數.(我們知道函數的名字是函數代碼塊的起始地址,這個首地址能夠將函數的控制權轉移給這個代碼塊的區域).在定義多個同名函數的時候
    發表于 10-01 17:18

    C++異常怎么解決

    早上好,在處理C++異常時,我遇到了一個問題:每次啟動異常后,用一個TI/catch塊處理它,MCU就會凍結。例如:討論的MCU是PIC32MX270F256D,編譯器是XC32 v1.40免費
    發表于 05-07 09:46

    異常安全的C++代碼編寫

    關于C++異常的爭論何其多也,但往往是一些不合事實的誤解。異常曾經是一個難以用好的語言特性,幸運的是,隨著C++社區經驗的積累,今天我們已經有足夠的知識輕松編寫異
    發表于 09-16 11:50 ?5次下載

    如何處理Linux下C++異常

     在C++中,無論何時在處理程序內捕獲一個異常,關于該異常來源的信息都是不為人知的。異常的具體來源可以提供許多更好地處理該異常的重
    發表于 07-15 09:44 ?2005次閱讀

    C++異常機制探討

    C++異常機制為我們提供了更好的解決方法。異常處理的基本思想是:當出現錯誤時拋出一個異常,希望它的調用者能捕獲并處理這個
    發表于 11-23 11:04 ?3480次閱讀
    <b class='flag-5'>C++</b><b class='flag-5'>異常</b><b class='flag-5'>機制</b>探討

    利用C++ ATL技術實現反射機制

    編制靈活的應用程序框架系統,反射機制是重要的實現手段。但由于C++本身沒有成熟的反射技術,對此進行了深入研究并提出一種實現方法。首先論述了反射機制的作用;然后描述了
    發表于 09-25 11:30 ?14次下載

    C++的G代碼解析算法研究

    進行編寫解析算法,把G 代碼作為一個對象,用類機制實現其解析功能,并使用GCC 作為其編譯器,提高編譯效率。可直接應用嵌入式,脫離PC+運動控制卡的限制。通過實驗的測試,以ARM 開發板為驗證實驗平臺,以arm-none-eab
    發表于 07-21 16:36 ?0次下載

    解析c++語言的Qt內省機制

    來實施多態。 c++ 的 內省 比較有限,它僅支持上面所說的型別 內省 ,? C++ 的型別 內省 是通過運行時類型識別(RTTI)(Run-Time Type Information)中的typeid 以及 dynamic_case關鍵字來實現的。
    發表于 12-15 10:22 ?1505次閱讀

    C語言的異常處理案例代碼

    相信很多朋友在此之前可能根本沒有使用或者聽說過C語言的異常處理,印象中都是C++或者java才有的東西,C語言怎么會有異常處理呢?
    的頭像 發表于 12-22 08:44 ?3842次閱讀

    如何深度解析C++拷貝構造函數詳細資料說明

    本文檔的主要內容詳細介紹的是如何深度解析C++拷貝構造函數詳細資料說明。
    發表于 07-05 17:41 ?0次下載
    如何深度<b class='flag-5'>解析</b><b class='flag-5'>C++</b>拷貝構造函數詳細資料說明

    C++異常機制底層原理與實際應用詳細說明

    我們在對 vector 做 push 操作的時候,或者對某個指針做 new 操作的時候,如果沒有做異常處理,一旦系統內存不夠用了,程序是會被 terminate 掉的。這就要求我們熟悉 C++ 異常,保證日常開發中能正確處理它。
    的頭像 發表于 11-22 11:34 ?3212次閱讀

    C++常見設計模式解析與實現

    C++常見設計模式解析與實現說明。
    發表于 06-01 15:44 ?11次下載

    詳解C/C++堆棧的工作機制

    參數,事實上是把參數壓入堆棧,聽起來,堆棧象一個大雜燴。那么,堆棧(Stack)到底是如何工作的呢?本文將詳解C/C++堆棧的工作機制。閱讀時請注意以下幾點:
    的頭像 發表于 07-29 09:09 ?1176次閱讀

    C++程序異常處理機制是什么

    那么C++設計了一套異常處理機制,一方面能夠使得異常處理和正常運行代碼進行分離,使得程序更加模塊化;另一方面,C++
    的頭像 發表于 02-21 10:37 ?901次閱讀
    <b class='flag-5'>C++</b>程序<b class='flag-5'>異常</b>處理<b class='flag-5'>機制</b>是什么
    主站蜘蛛池模板: 亚洲精品另类有吗中文字幕 | 免费成年人在线观看视频 | 大中国免费视频大全在线观看 | 国产中的精品AV一区二区 | 久久成人无码国产免费播放 | 99久久99久久久精品齐齐鬼色 | 向日葵视频app下载18岁以下勿看 | 国产亚洲福利精品一区 | 中文字幕在线观看网站 | 亚洲精品久久久久一区二区三 | 成人性生交大片免费看中文 | 九九热在线视频观看这里只有精品 | 日韩 国产 中文 无码 | 亚洲色大成网站WWW永久麻豆 | 国产欧美精品国产国产专区 | 亚洲精品中文字幕在线 | 午夜无码片在线观看影院 | 柠檬福利精品视频导航 | 久久怡红院国产精品 | 亚洲中文字幕手机版 | 丰满艳妇亲伦 | 亚洲综合小说久久另类区 | 国产成人免费a在线视频app | 狼人无码伊人AV啪啪 | 亚洲黄色大片 | 手机在线观看毛片 | 国产人妖一区二区 | a级毛片黄免费a级毛片 | 综合人妻久久一区二区精品 | 视频专区亚洲欧美日韩 | 色偷偷888欧美精品久久久 | 九色终合九色综合88 | 国语自产精品一区在线视频观看 | 国产日韩亚洲精品视频 | 国产精品JK白丝AV网站 | 精品三级久久久久电影网1 精品日韩视频 | 恋夜直播午夜秀场最新 | 日韩欧美群交P内射捆绑 | 女人爽得直叫免费视频 | 男人边吃奶边挵进去呻吟漫画 | 老熟风间由美AV在线一区二区 |