C++异常处理,Error,C和C++ 解决容错,栈自旋,Standard Exception【C++异常处理】(60)
- 異常處理
- Error
- C Error
- C和C++ 解決容錯
- 異常定義
- 語法格式
- 使用條例
- 異常流程測試
- 棧自旋
- unwinding
- RAII in Exception
- Standard Exception
- 異常自定義與拋出
- 標準異常
- 標準異常分類
- 標準異常使用
異常處理
Error
Error 譯為錯誤,常見的 Error 有編譯時 Error 和運行時 Error。運行時的 Error 又分為不可預料的邏輯錯誤和可以預料的運行異常。
可以預料的運行異常,常見的有,I/0,文件打開失敗,動態(tài)內存分配失敗,越界訪問等。
C Error
C 語言中錯誤的處理,通常采用返回值的方式或是置位全局變量的方式。這就存在兩個問題。如果返回值正是我們需要的數(shù)據,且返回數(shù)據同出錯數(shù)據 容錯性不高。全局變量,在多線程中易引發(fā)競爭。而且,當錯誤發(fā)生時,上級函數(shù)要出錯處理,層層上報,造成過多的出錯處理代碼,且傳遞的效率低下。
C++ 通過異常實現(xiàn)了返回與錯誤的處理的分離。
C和C++ 解決容錯
#include <iostream> #include <cmath> using namespace std; double triangleArea(double x, double y, double z) {double area;double s = (x + y + z) / 2;if (x + y > z && y + z > x && x + z > y) //邏輯代碼area = sqrt(s * (s - x) * (s - y) * (s - z));else //錯誤接口return -1.0;return area; } int main() {int a, b, c;float area;while (1){cin >> a >> b >> c;if (a > 0 && b > 0 && c > 0){area = triangleArea(a, b, c);if (area == -1.0)cout << "輸入的三角形不合法" << endl;elsecout << "Area:" << area << endl;}}return 0; }運行結果為:
上面函數(shù)triangleArea中把正常邏輯代碼和錯誤碼整合到一個函數(shù)接口進行返回。
那么C++里面對于錯誤就會單獨處理。
#include <iostream> #include <cmath> using namespace std; double triangleArea(double x, double y, double z) {double area;double s = (x + y + z) / 2;if (x + y > z && y + z > x && x + z > y)area = sqrt(s * (s - x) * (s - y) * (s - z));elsethrow - 1.0;return area; } int main() {int a, b, c;float area;try {while (1){cin >> a >> b >> c;if (a > 0 && b > 0 && c > 0){area = triangleArea(a, b, c);cout << "Area:" << area << endl;}}}catch (double e){cout << "return " << e << endl;cout << "輸入的三角形不合法" << endl;}return 0; }運行結果為:
上面代碼中,如果出現(xiàn)一場,就拋出異常。不和功能代碼進行整合。把錯誤處理和返回分離。
分析:
1,把可能發(fā)生異常的語句放在 try 語句聲當中。try 不影響原有語句的執(zhí)行流程。
2,若未發(fā)生異常,catch 子語句并不起作用。程序會流轉到 catch 子句的后面執(zhí)行。
3,若 try 塊中發(fā)生異常,則通過 throw 拋出異常。throw 拋出異常后,程序立即離開本函數(shù),轉到上一級函數(shù)。所以 triangleArea 函數(shù)中的 return 語句不會執(zhí)行。
圖解說明:
4,throw 拋出數(shù)據,類型不限。既可以是基本數(shù)據類型,也可以是構造數(shù)據類型。
5,程序流轉到 main 函數(shù)以后,try 語句塊中拋出進行匹配。匹配成功,執(zhí)行 catch語句,catch 語句執(zhí)行完畢后。繼續(xù)執(zhí)行后面的語句。
6,如無匹配,系統(tǒng)調用 terminate 終止程序。
#include <iostream> #include <cmath> using namespace std; double triangleArea(double x, double y, double z) {double area;double s = (x + y + z) / 2;if (x + y > z && y + z > x && x + z > y)area = sqrt(s * (s - x) * (s - y) * (s - z));elsethrow - 1;return area; } int main() {int a, b, c;float area;try {while (1){cin >> a >> b >> c;if (a > 0 && b > 0 && c > 0){area = triangleArea(a, b, c);cout << "Area:" << area << endl;}}}catch (double e){cout << "return " << e << endl;cout << "輸入的三角形不合法" << endl;}return 0; }上面代碼異常返回值為1,但是catch參數(shù)類型為double 那么直接會出現(xiàn)問題:
運行結果:
我們可以換一種寫法,catch參數(shù)為…
#include <iostream> #include <cmath> using namespace std; double triangleArea(double x, double y, double z) {double area;double s = (x + y + z) / 2;if (x + y > z && y + z > x && x + z > y)area = sqrt(s * (s - x) * (s - y) * (s - z));elsethrow - 1;return area; } int main() {int a, b, c;float area;try {while (1){cin >> a >> b >> c;if (a > 0 && b > 0 && c > 0){area = triangleArea(a, b, c);cout << "Area:" << area << endl;}}}catch (double e){cout << "return " << e << endl;cout << "輸入的三角形不合法" << endl;}catch (...){cout << "捕獲到了未知異常" << endl;}return 0; }運行結果為:
如果沒有任何捕獲,系統(tǒng)將會殺死程序運行。
異常定義
語法格式
try {被檢查可能拋出異常的語句 } catch(異常信息類型 [變量名]) {進行異常處理的語句 }使用條例
1,被檢語句必須放在 try 塊中,否則不起作用。
2,try catch 中花括號不可省。
3,一個 try-catch 結構中,只能有一個 try 塊,catch 塊卻可以有多個。以便與不同的類型信息匹配。
try{} catch(double){} catch(int){} catch(char){} catch(float){}4,throw 拋出的類型,既可以是系統(tǒng)預定義的標準類型也可以是自定義類型。從拋出到 catch 是一次復制拷貝的過程。如果有自定義類型,要考慮自定義類型的拷貝問題。
5,異常匹配,不作類型轉化。如果 catch 語句沒有匹配異常類型信息,就可以用(…)表示可以捕獲任何異常類型的信息。
catch(...) { cout<<"catch a unknow exception"<<endl; }6.try-catch 結構可以與 throw 在同一個函數(shù)中,也可以不在同一個函數(shù)中,throw拋出異常后,先在本函數(shù)內尋找與之匹配的 catch 塊,找不到與之匹配的就轉到上一層,如果上一層也沒有,則轉到更上一層的 catch 塊。如果最終找不到與之匹配的 catch 塊,系統(tǒng)則會調有系統(tǒng)函數(shù) terminate 使程序終止。
層級管理中,最好的處理方式就是在本層內解決,一旦出現(xiàn)異常層層上拋,工程量很大的時候就會打破層級管理的設計,所以不常使用。
異常流程測試
在中間環(huán)節(jié),來測試異常的流程。
#include <iostream>using namespace std;//通過聲明的方式,告知,調用方,如何處理 void func() throw(char) {throw 'a'; } //什么都沒有寫的情況-> 上拋 //寫點什么,處理自己可以處理的部分 ,若無匹配上拋 void foo() {try{func();}catch (int i){cout << "foo() catch " << i << endl;}catch (...) //若無匹配,寫日志上拋{cout << "log throw up" << endl;throw;} } int main() {try {foo();}catch (int i) {cout << "main() catch int " << i << endl;}catch (double i) {cout << "main() catch double " << i << endl;}return 0; }運行結果為:
如果拋出double類型,并且有double類型異常接受。
#include <iostream>using namespace std;//通過聲明的方式,告知,調用方,如何處理 void func() throw(double) {throw 1.1; } //什么都沒有寫的情況-> 上拋 //寫點什么,處理自己可以處理的部分 ,若無匹配上拋 void foo() {try{func();}catch (int i){cout << "foo() catch " << i << endl;}catch (...) //若無匹配,寫日志上拋{cout << "log throw up" << endl;throw;} } int main() {try {foo();}catch (int i) {cout << "main() catch int " << i << endl;}catch (double i) {cout << "main() catch double " << i << endl;}return 0; }運行結果為:
拋出異常聲明
(1)一個不拋擲任何類型異常的函數(shù)可以聲明為:
void func() throw(); //推薦使用
(2)如果在函數(shù)聲明中沒有包含異常接口聲明,則函數(shù)可以拋擲任何類型的異常,
例如:
void func(); //不推薦
(3)為了加強程序的可讀性,可以在函數(shù)聲明中列出可能拋出的所有異常類型。
例如:
void func() throw (A, B, C , D); //這個函數(shù) func()能夠且只能拋出類型 A B C D及其子類型的異常。
(4)如果一個函數(shù)拋出了它的異常接口聲明所不允許拋出的異常,該函數(shù)默認行為調用 terminate 函數(shù)中止程序。
代碼說明:
#include <iostream> using namespace std; class up {}; class down {}; void g() {throw 1; } //異常規(guī)格說明,f 函數(shù)只能拋出 up 和 down 類型的異常 void f(int i)throw(up, down) {switch (i) {case 1: throw up();case 2: throw down();}g(); } int main() {try {// f(1);f(4);}catch (...){cout << "捕獲一個未知異常" << endl;}return 0; }運行結果為:
棧自旋
unwinding
異常被拋出后,從進入 try 塊起,到異常被拋擲前,這期間在棧上的構造的所有對象,都會被自動析構。析構的順序與構造的順序相反。這一過程稱為棧的解旋(unwinding)。
#include <iostream> #include <memory> using namespace std; class A { public:A(){cout << "A constructor" << endl;}~A(){cout << "~A destructor" << endl;} }; int divide(int x, int y) {A a;if (y == 0)throw('a');return x / y; } void myDivide(int x, int y) {divide(x, y); } int main() {try {myDivide(4, 0);}catch (int x) {cout << "x" << endl;cout << x << endl;}catch (double y) {cout << "y" << endl;cout << y << endl;}catch (...){cout << "no x, no y" << endl;}return 0; }運行結果為:
我們可以看到棧上發(fā)生了析構。
我們對于代碼進行修改:
#include <iostream> #include <memory> using namespace std; class A { public:A(){cout << "A constructor" << endl;}~A(){cout << "~A destructor" << endl;} }; int divide(int x, int y) {A *p = new A;if (y == 0)throw('a');return x / y; } void myDivide(int x, int y) {divide(x, y); } int main() {try {myDivide(4, 0);}catch (int x) {cout << "x" << endl;cout << x << endl;}catch (double y) {cout << "y" << endl;cout << y << endl;}catch (...){cout << "no x, no y" << endl;}return 0; }運行結果為:
我們可以看到,如果是堆上的內存不會發(fā)生析構,那么就會導致內存泄露。
我們對于代碼進行修改:
運行結果為:
棧自旋也就是說,在拋出異常的時候要清棧,那么new的資源必須經過RAII包裹,否則就會泄露。
RAII in Exception
而堆上的空間,則會泄漏。利用遵循 RAII 思想的智能指針來解決。
Standard Exception
異常自定義與拋出
MyExcept
如果導致異常的因素有5個,我們不能拋出5次,所以就打包為一個類。
#include <iostream>using namespace std;class MyException { public:MyException(){cout << "MyException constructor" << endl;}MyException(const MyException&){cout << "MyException copy constructor" << endl;}~MyException(){cout << "~MyException destructor" << endl;} }; int divide(int x, int y) {if (y == 0)throw(MyException()); //拋出類return x / y; } void myDivide(int x, int y) { divide(x, y); } int main() {try {myDivide(4, 0);}catch (const MyException& a) { //& cout << "catch self define myexception" << endl;}return 0; }運行結果為:
我們可以看到一次構造和一次析構。
標準異常
標準異常分類
標準異常使用
#include <iostream> using namespace std; int main() {double* p;for (int i = 0; i < 1000; i++){p = new double[900000000];}return 0; }上面代碼會編譯會出現(xiàn)錯誤:
我們對于異常進行處理:
#include <iostream> using namespace std; int main() {try {double* p;for (int i = 0; i < 1000; i++){p = new double[900000];}}catch (std::bad_alloc & e) {cout << e.what() << endl;exit(-1);}return 0; }運行結果為:
總結
以上是生活随笔為你收集整理的C++异常处理,Error,C和C++ 解决容错,栈自旋,Standard Exception【C++异常处理】(60)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 打印helloworld,注释,从源文件
- 下一篇: C++IO流,istream statu