www.久久久久|狼友网站av天堂|精品国产无码a片|一级av色欲av|91在线播放视频|亚洲无码主播在线|国产精品草久在线|明星AV网站在线|污污内射久久一区|婷婷综合视频网站

當前位置:首頁 > 公眾號精選 > 嵌入式微處理器
[導(dǎo)讀]關(guān)于c++的異常處理,網(wǎng)上有很多的爭議,本文會介紹c++的異常處理的使用,以及我們應(yīng)該使用異常處理嗎,以及使用異常處理需要注意的地方。 什么是異常處理? 異常處理當然指的是對異常的處理,異常是指程序在執(zhí)行期間產(chǎn)生的問題,沒有按正確設(shè)想的流程走下去,

關(guān)于c++的異常處理,網(wǎng)上有很多的爭議,本文會介紹c++的異常處理的使用,以及我們應(yīng)該使用異常處理嗎,以及使用異常處理需要注意的地方。

什么是異常處理?

異常處理當然指的是對異常的處理,異常是指程序在執(zhí)行期間產(chǎn)生的問題,沒有按正確設(shè)想的流程走下去,比如除以零的操作,異常處理提供了一種轉(zhuǎn)移程序控制權(quán)的方式,這里涉及到三個關(guān)鍵字:

  • throw:當問題出現(xiàn)時,程序會通過throw來拋出一個異常

  • catch:在可能有throw想要處理問題的地方,通過catch關(guān)鍵字來捕獲異常

  • try:try塊中的代碼標識將被激活的特定異常,它后面通常跟著一個或多個catch塊

直接看示例代碼:

void func() { throw exception; // 拋出異常}
int main() { try { // try里放置可能拋出異常的代碼,塊中的代碼被稱為保護代碼 func(); } catch (exception1& e) { // 捕獲異常,異常類型為exception1 // code } catch (exception2& e) { // 捕獲異常,異常類型為exception2 // code } catch (...) { // code } return 0;}

c++標準都有什么異常?

C++ 提供了一系列標準的異常,定義在<exception> 中,我們可以在程序中使用這些標準的異常。它們是以父子類層次結(jié)構(gòu)組織起來的,如下所示:

圖片來自菜鳥教程

具體異常應(yīng)該不需要特別介紹了吧,看英文名字就可以知道大概意思。

自定義異常

可以通過繼承和重載exception類來自定義異常,見代碼:

#include <stdexcept>class MyException : public std::runtime_error {public: MyException() : std::runtime_error("MyException") { }};void f(){ // ... throw MyException();}
int main() { try { f(); } catch (MyException& e) { // ... } catch (...) { } return 0;}

我們應(yīng)該使用異常嗎?

在c++中關(guān)于是否使用異常一直都有爭議,典型的就是知乎上陳碩大神說的不應(yīng)該使用異常,還有就是google和美國國防部等都明確定義編碼規(guī)范來禁止在c++中使用異常,這里我找了很多中英文資料,在文末參考鏈接列舉了一些。

關(guān)于是否使用異常的討論帖子在這,https://www.zhihu.com/question/22889420

陳碩大神說的什么我就不貼出來了,他水平之高無需置疑,但他說的一些東西還是很有爭議的,關(guān)于異常處理,引用吳詠煒老師的一句話:“陳碩當然是個技術(shù)大牛。不過,在編程語言這件事上,我更愿意信任 Bjarne Stroustrup、Herb Sutter、Scott Meyers 和 Andrei Alexandrescu。這些大神們都認為異常是比返回錯誤碼更好的錯誤處理方式?!?/span>

而google明確禁用異常其實是有歷史包袱的,他們也認同異常處理是比錯誤碼更好的處理方式,但他們別無選擇,因為以前的編譯器對異常處理的不好,他們項目里面已經(jīng)有了大量的非異常安全的代碼,如果全改成異常處理的代碼是有很大的工作量的,具體可以看上面的鏈接和我文末引用的一些鏈接。

美國國防部禁用異常是出于實時性能考慮,工具鏈不能保證程序拋出異常時的實時性能,但國防部禁用了很多c++特性,例如內(nèi)存分配,我們真的追求飛機一樣的高性能嗎?

通過上面的介紹大家應(yīng)該能猜到我的結(jié)論了吧,當然這不是我的結(jié)論,而是大佬們的結(jié)論:推薦使用異常處理

異常處理有一些潛在的缺點:

  • 會有限的影響程序的性能,但正常工作流中不拋出異常的時候速度和普通函數(shù)一樣快,甚至更快

  • 會導(dǎo)致程序體積變大10%-20%,但我們真的那么在乎程序的體積嗎(除了移動端)

異常處理相對于使用錯誤碼的好處:

  • 如果不使用trycatch那就需要使用返回錯誤碼的方式,那就必然增加ifelse語句,每次函數(shù)返回后都會增加判斷的開銷,如果可以消除trycatch,代碼可能會更健壯,舉例如下:

void f1(){ try { // ... f2(); // ...} catch (some_exception& e) { // ...code that handles the error...}}void f2() { ...; f3(); ...; }void f3() { ...; f4(); ...; }void f4() { ...; f5(); ...; }void f5() { ...; f6(); ...; }void f6() { ...; f7(); ...; }void f7() { ...; f8(); ...; }void f8() { ...; f9(); ...; }void f9() { ...; f10(); ...; }void f10(){ // ... if ( /*...some error condition...*/ ) throw some_exception(); // ...}

而使用錯誤碼方式:

int f1(){ // ... int rc = f2(); if (rc == 0) { // ...} else { // ...code that handles the error...}}int f2(){ // ... int rc = f3(); if (rc != 0) return rc; // ... return 0;}int f3(){ // ... int rc = f4(); if (rc != 0) return rc; // ... return 0;}int f4(){ // ... int rc = f5(); if (rc != 0) return rc; // ... return 0;}int f5(){ // ... int rc = f6(); if (rc != 0) return rc; // ... return 0;}int f6(){ // ... int rc = f7(); if (rc != 0) return rc; // ... return 0;}int f7(){ // ... int rc = f8(); if (rc != 0) return rc; // ... return 0;}int f8(){ // ... int rc = f9(); if (rc != 0) return rc; // ... return 0;}int f9(){ // ... int rc = f10(); if (rc != 0) return rc; // ... return 0;}int f10(){ // ... if (...some error condition...) return some_nonzero_error_code; // ... return 0;}

錯誤碼方式對于問題的反向傳遞很麻煩,導(dǎo)致代碼腫脹,假如中間有一個環(huán)節(jié)忘記處理或處理有誤就會導(dǎo)致bug的產(chǎn)生,異常處理對于錯誤的處理更簡潔,可以更方便的把錯誤信息反饋給調(diào)用者,同時不需要調(diào)用者使用額外的ifelse分支來處理成功或者不成功的情況。

  • 一般來說使用錯誤碼方式標明函數(shù)是否成功執(zhí)行,一個值標明函數(shù)成功執(zhí)行,另外一個或者多個值標明函數(shù)執(zhí)行失敗,不同的錯誤碼標明不同的錯誤類型,調(diào)用者需要對不同的錯誤類型使用多個ifelse分支來處理。如果有更多ifelse,那么必然寫出更多測試用例,必然花費更多精力,導(dǎo)致項目晚上線。

拿數(shù)值運算代碼舉例:

class Number {public: friend Number operator+ (const Number& x, const Number& y); friend Number operator- (const Number& x, const Number& y); friend Number operator* (const Number& x, const Number& y); friend Number operator/ (const Number& x, const Number& y); // ...};

最簡單的可以這樣調(diào)用:

void f(Number x, Number y) { // ... Number sum = x + y; Number diff = x - y; Number prod = x * y; Number quot = x / y; // ...}

但是如果需要處理錯誤,例如除0或者數(shù)值溢出等,函數(shù)得到的就是錯誤的結(jié)果,調(diào)用者需要做處理。

先看使用錯誤碼的方式:

class Number {public: enum ReturnCode { Success, Overflow, Underflow, DivideByZero}; Number add(const Number& y, ReturnCode& rc) const; Number sub(const Number& y, ReturnCode& rc) const; Number mul(const Number& y, ReturnCode& rc) const; Number div(const Number& y, ReturnCode& rc) const; // ...};
int f(Number x, Number y){ // ... Number::ReturnCode rc; Number sum = x.add(y, rc); if (rc == Number::Overflow) { // ...code that handles overflow... return -1;} else if (rc == Number::Underflow) { // ...code that handles underflow... return -1;} else if (rc == Number::DivideByZero) { // ...code that handles divide-by-zero... return -1;} Number diff = x.sub(y, rc); if (rc == Number::Overflow) { // ...code that handles overflow... return -1;} else if (rc == Number::Underflow) { // ...code that handles underflow... return -1;} else if (rc == Number::DivideByZero) { // ...code that handles divide-by-zero... return -1;} Number prod = x.mul(y, rc); if (rc == Number::Overflow) { // ...code that handles overflow... return -1;} else if (rc == Number::Underflow) { // ...code that handles underflow... return -1;} else if (rc == Number::DivideByZero) { // ...code that handles divide-by-zero... return -1;} Number quot = x.div(y, rc); if (rc == Number::Overflow) { // ...code that handles overflow... return -1;} else if (rc == Number::Underflow) { // ...code that handles underflow... return -1;} else if (rc == Number::DivideByZero) { // ...code that handles divide-by-zero... return -1;} // ...}

再看使用異常處理的方式:

void f(Number x, Number y){ try { // ... Number sum = x + y; Number diff = x - y; Number prod = x * y; Number quot = x / y; // ...} catch (Number::Overflow& exception) { // ...code that handles overflow...} catch (Number::Underflow& exception) { // ...code that handles underflow...} catch (Number::DivideByZero& exception) { // ...code that handles divide-by-zero...}}

如果有更多的運算,或者有更多的錯誤碼,異常處理的優(yōu)勢會更明顯。

  • 使用異??梢允沟么a邏輯更清晰,將代碼按正確的邏輯列出來,邏輯更緊密代碼更容易讀懂,而錯誤處理可以單獨放到最后做處理。

  • 異??梢赃x擇自己處理或者傳遞給上層處理

異常處理的關(guān)鍵點

  1. 不應(yīng)該使用異常處理做什么?

  • throw僅用于拋出一個錯誤,標識函數(shù)沒有按設(shè)想的方式去執(zhí)行

  • 只有在知道可以處理錯誤時,才使用catch來捕獲錯誤,例如轉(zhuǎn)換類型或者內(nèi)存分配失敗

  • 不要使用throw來拋出編碼錯誤,應(yīng)該使用assert或者其它方法告訴編譯器或者崩潰進程收集debug信息

  • 如果有必須要崩潰的事件,或者無法恢復(fù)的問題,不應(yīng)該使用throw拋出,因為拋出來外部也無法處理,就應(yīng)該讓程序崩潰

  • try、catch不應(yīng)該簡單的用于函數(shù)返回值,函數(shù)的返回值應(yīng)該使用return操作,不應(yīng)該使用catch,這會給編程人員帶來誤解,同時也不應(yīng)該用異常來跳出循環(huán)

異常處理看似簡單好用,但它需要項目成員嚴格遵守開發(fā)規(guī)范,定好什么時候使用異常,什么時候不使用,而不是既使用異常又使用錯誤碼方式。

  • 構(gòu)造函數(shù)可以拋出異常嗎?可以而且建議使用異常,因為構(gòu)造函數(shù)沒有返回值,所以只能拋出異常,也有另一種辦法就是添加一個成員變量標識對象是否構(gòu)造成功,這種方法那就會額外添加一個返回該返回值的函數(shù),如果定義一個對象數(shù)組那就需要對數(shù)組每個對象都判斷是否構(gòu)造成功,這種代碼不太好。

  • 構(gòu)造函數(shù)拋出異常會產(chǎn)生內(nèi)存泄漏嗎?不會,構(gòu)造函數(shù)拋出異常產(chǎn)生內(nèi)存泄漏那是編譯器的bug,已經(jīng)在21世紀修復(fù),不要聽信謠言。

    void f() { X x; // If X::X() throws, the memory for x itself will not leak Y* p = new Y(); // If Y::Y() throws, the memory for *p itself will not leak}
  • 永遠不要在析構(gòu)函數(shù)中把異常拋出,還是拿對象數(shù)組舉例,數(shù)組里有多個對象,如果其中一個對象析構(gòu)過程中拋出異常,會導(dǎo)致剩余的對象都無法被析構(gòu),析構(gòu)函數(shù)應(yīng)該捕獲異常并把他們吞下或者終止程序,而不是拋出。

  • 構(gòu)造函數(shù)內(nèi)申請完資源后拋出異常怎么辦?使用智能指針,關(guān)于char*也可以使用std::string代替。


    #include <memory>
    using namespace std;
    class SPResourceClass {private: shared_ptr<int> m_p; shared_ptr<float> m_q;public: SPResourceClass() : m_p(new int), m_q(new float) { } // Implicitly defined dtor is OK for these members, // shared_ptr will clean up and avoid leaks regardless.};
  • 永遠通過值傳遞方式用throw拋出異常,通過引用傳遞用catch來捕獲異常。

  • 可以拋出基本類型也可以拋出對象,啥都可以

  • catch(...)可以捕獲所有異常

  • catch過程中不會觸發(fā)隱式類型轉(zhuǎn)換

  • 異常被拋出,但是直到main函數(shù)也沒有被catch,就會std::terminate()

  • c++不像java,不會強制檢查異常,throw了外層即使沒有catch也會編譯通過

  • 異常被拋出時,在catch之前,try和throw之間的所有局部對象都會被析構(gòu)

  • 如果一個成員函數(shù)不會產(chǎn)生任何異常,可以使用noexcept關(guān)鍵字修飾

  • 通過throw可以重新拋出異常

    int main(){ try { try { throw 20; } catch (int n) { cout << "Handle Partially "; throw; //Re-throwing an exception } } catch (int n) { cout << "Handle remaining "; } return 0;}

    小測驗

    你真的理解異常處理了嗎,我們可以做幾道測驗題:

    看這幾段代碼會輸出什么:

    測試代碼1:

    #include <iostream>using namespace std; int main(){ int x = -1;  // Some code cout << "Before try \n"; try { cout << "Inside try \n"; if (x < 0) { throw x; cout << "After throw (Never executed) \n"; } } catch (int x ) { cout << "Exception Caught \n"; }  cout << "After catch (Will be executed) \n"; return 0;}

    輸出:

    Before tryInside tryException CaughtAfter catch (Will be executed)

    throw后面的代碼不會被執(zhí)行

    測試代碼2:

    #include <iostream>using namespace std; int main(){ try { throw 10; } catch (char *excp) { cout << "Caught " << excp; } catch (...) { cout << "Default Exception\n"; } return 0;}

    輸出:

    Default Exception

    throw出來的10首先沒有匹配char*,而catch(...)可以捕獲所有異常。

    測試代碼3:

    #include <iostream>using namespace std; int main(){ try { throw 'a'; } catch (int x) { cout << "Caught " << x; } catch (...) { cout << "Default Exception\n"; } return 0;}

    輸出:

    Default Exception

    'a'是字符,不能隱式轉(zhuǎn)換為int型,所以還是匹配到了...中。

    測試代碼4:

    #include <iostream>using namespace std; int main(){ try { throw 'a'; } catch (int x) { cout << "Caught "; } return 0;}

    程序崩潰,因為拋出的異常直到main函數(shù)也沒有被捕獲,std::terminate()就會被調(diào)用來終止程序。

    測試代碼5:

    #include <iostream>using namespace std; int main(){ try { try { throw 20; } catch (int n) { cout << "Handle Partially "; throw; //Re-throwing an exception } } catch (int n) { cout << "Handle remaining "; } return 0;}

    輸出:

    Handle Partially Handle remaining

    catch中的throw會重新拋出異常。

    測試代碼6:

    #include <iostream>using namespace std; class Test {public: Test() { cout << "Constructor of Test " << endl; } ~Test() { cout << "Destructor of Test " << endl; }}; int main() { try { Test t1; throw 10;} catch(int i) { cout << "Caught " << i << endl;}}

    輸出:

    Constructor of TestDestructor of TestCaught 10

    在拋出異常被捕獲之前,try和throw中的局部變量會被析構(gòu)。

    小總結(jié)

    異常處理對于錯誤的處理更簡潔,可以更方便的把錯誤信息反饋給調(diào)用者,同時不需要調(diào)用者使用額外的ifelse分支來處理成功或者不成功的情況。如果不是特別特別注重實時性能或者特別在乎程序的體積我們完全可以使用異常處理替代我們平時使用的c語言中的那種錯誤碼處理方式。

    關(guān)于c++的異常處理就介紹到這里,你都了解了嗎?大家有問題可以

    參考資料

    https://www.zhihu.com/question/22889420
    https://isocpp.org/wiki/faq/
    https://docs.microsoft.com/en-us/cpp/cpp/errors-and-exception-handling-modern-cpp?view=vs-2019
    https://blog.csdn.net/zhangyifei216/article/details/50410314
    https://www.runoob.com/cplusplus/cpp-exceptions-handling.html
    https://www.geeksforgeeks.org/exception-handling-c/


    本文授權(quán)轉(zhuǎn)載自公眾號“程序喵大人”,作者程序喵大人


    -END-




    推薦閱讀



    【01】C++ 基礎(chǔ)知識!初學(xué)者必看!
    【02】C++ 轉(zhuǎn) Python 這三年,我都經(jīng)歷了什么?
    【03】C 語言會比 C++ 快?
    【04】2019年C++有哪些發(fā)展?
    【05】C++編程中的核心知識點


    免責聲明:整理文章為傳播相關(guān)技術(shù),版權(quán)歸原作者所有,如有侵權(quán),請聯(lián)系刪除

    免責聲明:本文內(nèi)容由21ic獲得授權(quán)后發(fā)布,版權(quán)歸原作者所有,本平臺僅提供信息存儲服務(wù)。文章僅代表作者個人觀點,不代表本平臺立場,如有問題,請聯(lián)系我們,謝謝!

    嵌入式ARM

    掃描二維碼,關(guān)注更多精彩內(nèi)容

    本站聲明: 本文章由作者或相關(guān)機構(gòu)授權(quán)發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點,本站亦不保證或承諾內(nèi)容真實性等。需要轉(zhuǎn)載請聯(lián)系該專欄作者,如若文章內(nèi)容侵犯您的權(quán)益,請及時聯(lián)系本站刪除。
    換一批
    延伸閱讀

    9月2日消息,不造車的華為或?qū)⒋呱龈蟮莫毥谦F公司,隨著阿維塔和賽力斯的入局,華為引望愈發(fā)顯得引人矚目。

    關(guān)鍵字: 阿維塔 塞力斯 華為

    加利福尼亞州圣克拉拉縣2024年8月30日 /美通社/ -- 數(shù)字化轉(zhuǎn)型技術(shù)解決方案公司Trianz今天宣布,該公司與Amazon Web Services (AWS)簽訂了...

    關(guān)鍵字: AWS AN BSP 數(shù)字化

    倫敦2024年8月29日 /美通社/ -- 英國汽車技術(shù)公司SODA.Auto推出其旗艦產(chǎn)品SODA V,這是全球首款涵蓋汽車工程師從創(chuàng)意到認證的所有需求的工具,可用于創(chuàng)建軟件定義汽車。 SODA V工具的開發(fā)耗時1.5...

    關(guān)鍵字: 汽車 人工智能 智能驅(qū)動 BSP

    北京2024年8月28日 /美通社/ -- 越來越多用戶希望企業(yè)業(yè)務(wù)能7×24不間斷運行,同時企業(yè)卻面臨越來越多業(yè)務(wù)中斷的風險,如企業(yè)系統(tǒng)復(fù)雜性的增加,頻繁的功能更新和發(fā)布等。如何確保業(yè)務(wù)連續(xù)性,提升韌性,成...

    關(guān)鍵字: 亞馬遜 解密 控制平面 BSP

    8月30日消息,據(jù)媒體報道,騰訊和網(wǎng)易近期正在縮減他們對日本游戲市場的投資。

    關(guān)鍵字: 騰訊 編碼器 CPU

    8月28日消息,今天上午,2024中國國際大數(shù)據(jù)產(chǎn)業(yè)博覽會開幕式在貴陽舉行,華為董事、質(zhì)量流程IT總裁陶景文發(fā)表了演講。

    關(guān)鍵字: 華為 12nm EDA 半導(dǎo)體

    8月28日消息,在2024中國國際大數(shù)據(jù)產(chǎn)業(yè)博覽會上,華為常務(wù)董事、華為云CEO張平安發(fā)表演講稱,數(shù)字世界的話語權(quán)最終是由生態(tài)的繁榮決定的。

    關(guān)鍵字: 華為 12nm 手機 衛(wèi)星通信

    要點: 有效應(yīng)對環(huán)境變化,經(jīng)營業(yè)績穩(wěn)中有升 落實提質(zhì)增效舉措,毛利潤率延續(xù)升勢 戰(zhàn)略布局成效顯著,戰(zhàn)新業(yè)務(wù)引領(lǐng)增長 以科技創(chuàng)新為引領(lǐng),提升企業(yè)核心競爭力 堅持高質(zhì)量發(fā)展策略,塑強核心競爭優(yōu)勢...

    關(guān)鍵字: 通信 BSP 電信運營商 數(shù)字經(jīng)濟

    北京2024年8月27日 /美通社/ -- 8月21日,由中央廣播電視總臺與中國電影電視技術(shù)學(xué)會聯(lián)合牽頭組建的NVI技術(shù)創(chuàng)新聯(lián)盟在BIRTV2024超高清全產(chǎn)業(yè)鏈發(fā)展研討會上宣布正式成立。 活動現(xiàn)場 NVI技術(shù)創(chuàng)新聯(lián)...

    關(guān)鍵字: VI 傳輸協(xié)議 音頻 BSP

    北京2024年8月27日 /美通社/ -- 在8月23日舉辦的2024年長三角生態(tài)綠色一體化發(fā)展示范區(qū)聯(lián)合招商會上,軟通動力信息技術(shù)(集團)股份有限公司(以下簡稱"軟通動力")與長三角投資(上海)有限...

    關(guān)鍵字: BSP 信息技術(shù)
    關(guān)閉
    關(guān)閉