日本黄色一级经典视频|伊人久久精品视频|亚洲黄色色周成人视频九九九|av免费网址黄色小短片|黄色Av无码亚洲成年人|亚洲1区2区3区无码|真人黄片免费观看|无码一级小说欧美日免费三级|日韩中文字幕91在线看|精品久久久无码中文字幕边打电话

當前位置:首頁 > > 嵌入式微處理器
[導(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)勢會更明顯。

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

  • 異??梢赃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)系本站刪除。
    換一批
    延伸閱讀

    LED驅(qū)動電源的輸入包括高壓工頻交流(即市電)、低壓直流、高壓直流、低壓高頻交流(如電子變壓器的輸出)等。

    關(guān)鍵字: 驅(qū)動電源

    在工業(yè)自動化蓬勃發(fā)展的當下,工業(yè)電機作為核心動力設(shè)備,其驅(qū)動電源的性能直接關(guān)系到整個系統(tǒng)的穩(wěn)定性和可靠性。其中,反電動勢抑制與過流保護是驅(qū)動電源設(shè)計中至關(guān)重要的兩個環(huán)節(jié),集成化方案的設(shè)計成為提升電機驅(qū)動性能的關(guān)鍵。

    關(guān)鍵字: 工業(yè)電機 驅(qū)動電源

    LED 驅(qū)動電源作為 LED 照明系統(tǒng)的 “心臟”,其穩(wěn)定性直接決定了整個照明設(shè)備的使用壽命。然而,在實際應(yīng)用中,LED 驅(qū)動電源易損壞的問題卻十分常見,不僅增加了維護成本,還影響了用戶體驗。要解決這一問題,需從設(shè)計、生...

    關(guān)鍵字: 驅(qū)動電源 照明系統(tǒng) 散熱

    根據(jù)LED驅(qū)動電源的公式,電感內(nèi)電流波動大小和電感值成反比,輸出紋波和輸出電容值成反比。所以加大電感值和輸出電容值可以減小紋波。

    關(guān)鍵字: LED 設(shè)計 驅(qū)動電源

    電動汽車(EV)作為新能源汽車的重要代表,正逐漸成為全球汽車產(chǎn)業(yè)的重要發(fā)展方向。電動汽車的核心技術(shù)之一是電機驅(qū)動控制系統(tǒng),而絕緣柵雙極型晶體管(IGBT)作為電機驅(qū)動系統(tǒng)中的關(guān)鍵元件,其性能直接影響到電動汽車的動力性能和...

    關(guān)鍵字: 電動汽車 新能源 驅(qū)動電源

    在現(xiàn)代城市建設(shè)中,街道及停車場照明作為基礎(chǔ)設(shè)施的重要組成部分,其質(zhì)量和效率直接關(guān)系到城市的公共安全、居民生活質(zhì)量和能源利用效率。隨著科技的進步,高亮度白光發(fā)光二極管(LED)因其獨特的優(yōu)勢逐漸取代傳統(tǒng)光源,成為大功率區(qū)域...

    關(guān)鍵字: 發(fā)光二極管 驅(qū)動電源 LED

    LED通用照明設(shè)計工程師會遇到許多挑戰(zhàn),如功率密度、功率因數(shù)校正(PFC)、空間受限和可靠性等。

    關(guān)鍵字: LED 驅(qū)動電源 功率因數(shù)校正

    在LED照明技術(shù)日益普及的今天,LED驅(qū)動電源的電磁干擾(EMI)問題成為了一個不可忽視的挑戰(zhàn)。電磁干擾不僅會影響LED燈具的正常工作,還可能對周圍電子設(shè)備造成不利影響,甚至引發(fā)系統(tǒng)故障。因此,采取有效的硬件措施來解決L...

    關(guān)鍵字: LED照明技術(shù) 電磁干擾 驅(qū)動電源

    開關(guān)電源具有效率高的特性,而且開關(guān)電源的變壓器體積比串聯(lián)穩(wěn)壓型電源的要小得多,電源電路比較整潔,整機重量也有所下降,所以,現(xiàn)在的LED驅(qū)動電源

    關(guān)鍵字: LED 驅(qū)動電源 開關(guān)電源

    LED驅(qū)動電源是把電源供應(yīng)轉(zhuǎn)換為特定的電壓電流以驅(qū)動LED發(fā)光的電壓轉(zhuǎn)換器,通常情況下:LED驅(qū)動電源的輸入包括高壓工頻交流(即市電)、低壓直流、高壓直流、低壓高頻交流(如電子變壓器的輸出)等。

    關(guān)鍵字: LED 隧道燈 驅(qū)動電源
    關(guān)閉