1.C++ 異常概念
異常是一種處理錯誤的方式,當一個函數發現自己無法處理的錯誤時就可以拋出異常,讓函數的直接或間接的調用者處理這個錯誤。
- throw:當問題出現時,程序會拋出一個異常。這是通過使用?
throw
?關鍵字來完成的。 - catch:在您想要處理問題的地方,通過異常處理程序捕獲異常。
catch
?關鍵字用于捕獲異常,可以有多個?catch
?進行捕獲。 - try:
try
?塊中的代碼標識將被激活的特定異常,它后面通常跟著一個或多個?catch
?塊。
如果有一個塊拋出一個異常,捕獲異常的方法會使用?try
?和?catch
?關鍵字。try
?塊中放置可能拋出異常的代碼,try
?塊中的代碼被稱為保護代碼。
2. 異常的拋出和捕獲
異常的拋出和匹配原則
- 異常是通過拋出對象而引發的,該對象的類型決定了應該激活哪個?
catch
?的處理代碼。 - 被選中的處理代碼是調用鏈中與該對象類型匹配且離拋出異常位置最近的那一個。
- 拋出異常對象后,會生成一個異常對象的拷貝,因為拋出的異常對象可能是一個臨時對象,所以會生成一個拷貝對象,這個拷貝的臨時對象會在被?
catch
?以后銷毀。(這里的處理類似于函數的傳值返回) catch(...)
?可以捕獲任意類型的異常,問題是不知道異常錯誤是什么。- 實際中拋出和捕獲的匹配原則有個例外,并不都是類型完全匹配,可以拋出的派生類對象,使用基類捕獲,這個在實際中非常實用,我們后面會詳細講解這個。
在函數調用鏈中異常棧展開匹配原則
- 首先檢查?
throw
?本身是否在?try
?塊內部,如果是再查找匹配的?catch
?語句。如果有匹配的,則調到?catch
?的地方進行處理。 - 沒有匹配的?
catch
?則退出當前函數棧,繼續在調用函數的棧中進行查找匹配的?catch
。 - 如果到達?
main
?函數的棧,依舊沒有匹配的,則終止程序。上述這個沿著調用鏈查找匹配的?catch
?子句的過程稱為棧展開。所以實際中我們最后都要加一個?catch(...)
?捕獲任意類型的異常,否則當有異常沒捕獲,程序就會直接終止。 - 找到匹配的?
catch
?子句并處理以后,會繼續沿著?catch
?子句后面繼續執行。
代碼展示
void exe()
{throw 1;
}
void func()
{int x = 2, y = 0;auto ret = [=]()->int {if (y == 0) { throw " 除0錯誤"; } else { return x / y; }};std::cout << ret() << std::endl;
}
int main()
{try{func();exe();}catch (const char*tsr){cerr <<tsr << endl;}catch (...){cerr << "未知異常!" << endl;}return 0;
}
下面展示上面的代碼進階版也就是,當你捕獲到異常進行緊急修復,然后重新拋出!重新拋出的異常會被調用鏈上層捕捉!切記這個調用鏈是給上走的!
void exe() {throw 1;
}void func() {int x = 2, y = 0;auto ret = [=]()->int {if (y == 0) {// 拋出標準異常類型,便于統一處理throw std::runtime_error("除0錯誤");} else {return x / y;}};try {std::cout << ret() << std::endl;} catch (const std::runtime_error& e) {std::cerr << e.what() << std::endl;// 修復除零錯誤,將除數修改為非零值y = 1;auto newRet = [x, y]()->int { return x / y; };std::cout << "修復后結果: " << newRet() << std::endl;// 重新拋出異常throw;}
}int main() {try {func();exe();} catch (const std::runtime_error& e) {std::cerr << "main函數捕獲到重新拋出的異常: " << e.what() << std::endl;std::cout << "異常已在下層函數修復,程序繼續執行" << std::endl;} catch (int e) {std::cerr << "捕獲到異常: " << e << std::endl;// 這里可以添加對異常值為 1 的具體處理邏輯std::cout << "異常已處理,程序繼續執行" << std::endl;} catch (...) {std::cerr << "未知異常!" << std::endl;}return 0;
}
3.繼承拋異常
//
//在實際當中有一個拋出和捕獲特例而且也非常常用!可以拋出派生類對象,讓基類去捕獲
#include <iostream>
#include <thread>
#include <chrono>
#include <cstdlib>
#include <ctime>
#include <string>// 假設存在基類 Exception,這里未給出完整定義,實際需包含相關頭文件或完整實現
void SQLMgr();
//公司常用的異常繼承體系!
class Exception
{
public:Exception(const std::string& errmsg, int id) : _errmsg(errmsg), _id(id) {}virtual std::string what() const = 0;
protected:std::string _errmsg;int _id;
};class HttpServerException : public Exception {
public:HttpServerException(const std::string& errmsg, int id, const std::string& type): Exception(errmsg, id), _type(type) {}virtual std::string what() const override {std::string str = "HttpServerException:";str += _type;str += ":";str += _errmsg;return str;}
private:const std::string _type;
};class CacheException : public Exception {
public:CacheException(const std::string& errmsg, int id): Exception(errmsg, id) {}virtual std::string what() const override {std::string str = "CacheException:";str += _errmsg;return str;}
};class SqlException : public Exception {
public:SqlException(const std::string& errmsg, int id, const std::string& sql): Exception(errmsg, id), _sql(sql) {}virtual std::string what() const override {std::string str = "SqlException:";str += _errmsg;str += "->";str += _sql;return str;}
private:const std::string _sql;
};void CacheMgr() {srand(time(0));if (rand() % 5 == 0) {throw CacheException("權限不足", 100);}else if (rand() % 6 == 0) {throw CacheException("數據不存在", 101);}SQLMgr();
}void HttpServer() {srand(time(0));if (rand() % 3 == 0) {throw HttpServerException("請求資源不存在", 100, "get");}else if (rand() % 4 == 0) {throw HttpServerException("權限不足", 101, "post");}CacheMgr();
}void SQLMgr() {srand(time(0));if (rand() % 7 == 0) {throw SqlException("權限不足", 100, "select * from name = '張三'");}//throw "xxxxxx";
}int main() {while (1) {std::this_thread::sleep_for(std::chrono::seconds(1));try {HttpServer();}catch (const Exception& e) //捕獲父類對象{std::cout << e.what() << std::endl;}catch (...) {std::cout << "Unkown Exception" << std::endl;}}return 0;
}