深圳網(wǎng)站seo教程簡(jiǎn)述提升關(guān)鍵詞排名的方法
一、C 語(yǔ)言傳統(tǒng)的錯(cuò)誤處理方式
在 C 語(yǔ)言中,處理錯(cuò)誤主要有兩種傳統(tǒng)方式,每種方式都有其特點(diǎn)和局限性。
1. 終止程序
- 原理:使用類(lèi)似
assert
這樣的斷言機(jī)制,當(dāng)程序運(yùn)行到某個(gè)條件不滿(mǎn)足時(shí),直接終止程序的執(zhí)行。 - 示例代碼
#include <assert.h> #include <stdio.h>void divide(int a, int b) {assert(b != 0); // 斷言b不為0,如果b為0,程序會(huì)終止printf("%d / %d = %d\n", a, b, a / b); }int main() {divide(10, 0);return 0; }
- 缺陷:這種方式對(duì)用戶(hù)不太友好,因?yàn)橐坏┏霈F(xiàn)錯(cuò)誤,程序會(huì)直接崩潰,用戶(hù)無(wú)法進(jìn)行一些必要的處理或恢復(fù)操作。例如在發(fā)生內(nèi)存錯(cuò)誤、除 0 錯(cuò)誤等情況時(shí),程序會(huì)突然終止,給用戶(hù)帶來(lái)不好的體驗(yàn)。
2. 返回錯(cuò)誤碼
- 原理:函數(shù)在執(zhí)行過(guò)程中,如果遇到錯(cuò)誤,會(huì)返回一個(gè)特定的錯(cuò)誤碼。程序員需要根據(jù)這個(gè)錯(cuò)誤碼去查找對(duì)應(yīng)的錯(cuò)誤信息。在系統(tǒng)的很多庫(kù)的接口函數(shù)中,會(huì)把錯(cuò)誤碼放到
errno
變量中,表示發(fā)生的錯(cuò)誤。 - 示例代碼
#include <stdio.h> #include <errno.h> #include <string.h>int divide(int a, int b) {if (b == 0) {errno = EINVAL; // 設(shè)置錯(cuò)誤碼為無(wú)效參數(shù)return -1;}return a / b; }int main() {int result = divide(10, 0);if (result == -1) {printf("Error: %s\n", strerror(errno)); // 根據(jù)錯(cuò)誤碼輸出錯(cuò)誤信息} else {printf("Result: %d\n", result);}return 0; }
?
- 缺陷:需要程序員手動(dòng)去查找和處理錯(cuò)誤碼對(duì)應(yīng)的錯(cuò)誤信息,增加了開(kāi)發(fā)的復(fù)雜度。而且不同的庫(kù)可能使用不同的錯(cuò)誤碼體系,容易造成混淆。
實(shí)際應(yīng)用情況
在實(shí)際的 C 語(yǔ)言編程中,基本都是使用返回錯(cuò)誤碼的方式處理錯(cuò)誤,只有在處理非常嚴(yán)重的錯(cuò)誤時(shí),才會(huì)使用終止程序的方式。
二、C++ 異常概念
C++ 引入了異常機(jī)制,為錯(cuò)誤處理提供了一種更加靈活和強(qiáng)大的方式。當(dāng)一個(gè)函數(shù)發(fā)現(xiàn)自己無(wú)法處理的錯(cuò)誤時(shí),可以?huà)伋霎惓?#xff0c;讓函數(shù)的直接或間接調(diào)用者來(lái)處理這個(gè)錯(cuò)誤。
1. 異常處理的關(guān)鍵字
throw
:當(dāng)問(wèn)題出現(xiàn)時(shí),程序會(huì)拋出一個(gè)異常。通過(guò)使用throw
關(guān)鍵字來(lái)完成,后面可以跟任意類(lèi)型的數(shù)據(jù),如整數(shù)、字符串、自定義對(duì)象等。catch
:用于捕獲異常。可以有多個(gè)catch
塊,每個(gè)catch
塊可以捕獲不同類(lèi)型的異常。try
:try
塊中的代碼標(biāo)識(shí)將被激活的特定異常,它后面通常跟著一個(gè)或多個(gè)catch
塊。try
塊中的代碼被稱(chēng)為保護(hù)代碼。
2.?try/catch
語(yǔ)句的語(yǔ)法
try
{// 保護(hù)的標(biāo)識(shí)代碼
}
catch( ExceptionName e1 )
{// catch 塊,處理類(lèi)型為ExceptionName的異常
}
catch( ExceptionName e2 )
{// catch 塊,處理類(lèi)型為ExceptionName的異常
}
catch( ExceptionName eN )
{// catch 塊,處理類(lèi)型為ExceptionName的異常
}
?
3. 示例代碼?
#include <iostream>
using namespace std;// 定義一個(gè)除法函數(shù),可能會(huì)拋出異常
double Division(int a, int b) {// 當(dāng)b == 0時(shí)拋出異常if (b == 0) {throw "Division by zero condition!";} else {return ((double)a / (double)b);}
}// 定義一個(gè)函數(shù),調(diào)用Division函數(shù)并處理異常
void Func() {try {int len, time;cout << "請(qǐng)輸入兩個(gè)整數(shù)(用空格分隔): ";cin >> len >> time;cout << "除法結(jié)果: " << Division(len, time) << endl;}catch (const char* errmsg) {cout << "捕獲到異常: " << errmsg << endl;}}int main() {try {Func();}catch (const char* errmsg) {cout << "捕獲到字符串類(lèi)型異常: " << errmsg << endl;}return 0;
}
三、異常處理基礎(chǔ)
1. 異常拋出與捕獲原則
-
拋出機(jī)制:通過(guò)拋出對(duì)象觸發(fā)異常(可拋出任意類(lèi)型對(duì)象)
-
匹配規(guī)則:
-
匹配類(lèi)型相同且位置最近的catch塊
#include <iostream>// 函數(shù)B拋出整數(shù)類(lèi)型異常 void functionB() {throw 42; }// 函數(shù)A調(diào)用函數(shù)B,并嘗試捕獲異常 // functionB? 拋出一個(gè) ?int? 類(lèi)型異常。 void functionA() {try {functionB();} catch (double) {std::cout << "在functionA中捕獲到double類(lèi)型異常" << std::endl;} catch (int num) {//與第二個(gè) ?catch? 塊匹配,且它離異常拋出點(diǎn) ?functionB? 最近,//所以會(huì)執(zhí)行該 catch?塊,輸出“在functionA中捕獲到int類(lèi)型異常,//值為: 42”。std::cout << "在functionA中捕獲到int類(lèi)型異常,值為: " << num << std::endl;} }int main() {try {functionA();} catch (...) {//如果functionA中沒(méi)有匹配int類(lèi)型的catch塊,異常會(huì)傳遞到main函數(shù),//main函數(shù)中的 ?catch(...)? 會(huì)捕獲所有類(lèi)型異常。std::cout << "在main中捕獲到其他類(lèi)型異常" << std::endl;}return 0; }
-
會(huì)生成異常對(duì)象的拷貝(保證異常對(duì)象有效性)
// 拋出字符串異常示例 double Division(int a, int b) {if (b == 0) {string s("Division by zero!");throw s; // 拋出拷貝后的臨時(shí)對(duì)象}return static_cast<double>(a)/b; }void func() {int x, y;cin >> x >> y;cout << Division(x, y) << endl; }int main() {while (true) {try {func();}catch (const string& err) { // 捕獲引用避免拷貝cout << "Error: " << err << endl;}} }
-
派生類(lèi)異??捎没?lèi)捕獲(實(shí)際開(kāi)發(fā)常用方式)
-
2. 通用捕獲與繼承體系
// 通用捕獲與繼承示例
class BaseException {};
class MathException : public BaseException {};int main() {try {throw MathException();}catch (const BaseException&) { // 基類(lèi)捕獲派生類(lèi)異常cout << "Base exception caught" << endl; }catch (...) { // 最后防線(xiàn)捕獲所有異常cout << "Unknown exception" << endl;}
}
四、異常傳播機(jī)制
1. 棧展開(kāi)過(guò)程
-
檢查throw所在try塊
-
逐層回退調(diào)用棧查找匹配catch
-
到達(dá)main未匹配則程序終止
-
異常處理后繼續(xù)執(zhí)行catch塊后續(xù)代碼
2. 異常重新拋出
double Division(int a, int b)
{// 當(dāng)b == 0時(shí)拋出異常if (b == 0){throw "Division by zero condition!";}return (double)a / (double)b;
}
void Func()
{// 這里可以看到如果發(fā)生除0錯(cuò)誤拋出異常,另外下面的array沒(méi)有得到釋放。// 所以這里捕獲異常后并不處理異常,異常還是交給外面處理,這里捕獲了再// 重新拋出去。int* array = new int[10];try {int len, time;cin >> len >> time;cout << Division(len, time) << endl;}catch (...){cout << "delete []" << array << endl;delete[] array;throw;}// ...cout << "delete []" << array << endl;delete[] array;
}int main()
{try{Func();}catch (const char* errmsg){cout << errmsg << endl;}return 0;
}
五、異常安全規(guī)范
1. 關(guān)鍵原則
-
構(gòu)造函數(shù):避免拋出異常(可能導(dǎo)致對(duì)象不完整)
-
析構(gòu)函數(shù):禁止拋出異常(防止資源泄漏)
-
RAII機(jī)制:通過(guò)智能指針等實(shí)現(xiàn)資源自動(dòng)管理
2.異常規(guī)范
- 異常規(guī)格說(shuō)明的目的是為了讓函數(shù)使用者知道該函數(shù)可能拋出的異常有哪些。 可以在函數(shù)的 后面接throw(類(lèi)型),列出這個(gè)函數(shù)可能拋擲的所有異常類(lèi)型。
- 函數(shù)的后面接throw(),表示函數(shù)不拋異常。
- 若無(wú)異常接口聲明,則此函數(shù)可以?huà)仈S任何類(lèi)型的異常。
// 這里表示這個(gè)函數(shù)會(huì)拋出A/B/C/D中的某種類(lèi)型的異常
void fun() throw(A,B,C,D);// 這里表示這個(gè)函數(shù)只會(huì)拋出bad_alloc的異常
void* operator new (std::size_t size) throw (std::bad_alloc);// 這里表示這個(gè)函數(shù)不會(huì)拋出異常
void* operator delete (std::size_t size, void* ptr) throw();// C++11 中新增的noexcept,表示不會(huì)拋異常
thread() noexcept;
thread (thread&& x) noexcept;
六、自定義異常體系設(shè)計(jì)
1. 服務(wù)器開(kāi)發(fā)典型繼承體系
在服務(wù)器開(kāi)發(fā)中,為了更好地管理和處理不同類(lèi)型的異常,通常會(huì)設(shè)計(jì)一個(gè)異常繼承體系。以下是一個(gè)示例代碼:
// 異?;?lèi)(抽象錯(cuò)誤類(lèi)型)
// Exception 類(lèi):作為基類(lèi)異常,包含異常信息 _errmsg 和異常編號(hào) _id,
// 并定義了虛函數(shù) what() 用于返回異常信息,支持多態(tài)。
class Exception {
public:Exception(const string& errmsg, int id): _errmsg(errmsg), _id(id) {}virtual string what() const {return "[" + to_string(_id) + "] " + _errmsg;}virtual ~Exception() = default; // 虛析構(gòu)保證正確釋放protected:string _errmsg; // 錯(cuò)誤描述int _id; // 錯(cuò)誤編號(hào)
};// SQL操作異常(具體錯(cuò)誤類(lèi)型)
// SqlException 類(lèi):繼承自 Exception 類(lèi),添加了 _sql 成員變量,重寫(xiě)了
// what() 函數(shù),返回更詳細(xì)的 SQL 異常信息。
class SqlException : public Exception {
public:SqlException(const string& errmsg, int id, const string& sql): Exception(errmsg, id), _sql(sql) {}virtual string what() const override {return Exception::what() + "\n[SQL] " + _sql;}private:string _sql; // 錯(cuò)誤關(guān)聯(lián)的SQL語(yǔ)句
};// 緩存異常類(lèi),繼承自 Exception
// CacheException 類(lèi):繼承自 Exception 類(lèi),重寫(xiě)了 what() 函數(shù),返回緩存異常信息
class CacheException : public Exception {
public:CacheException(const std::string& errmsg, int id): Exception(errmsg, id){}// 重寫(xiě) what 函數(shù),返回緩存異常信息virtual std::string what() const {std::string str = "CacheException:";str += _errmsg;return str;}
};// HTTP服務(wù)異常(具體錯(cuò)誤類(lèi)型)
// HttpServerException 類(lèi):繼承自 Exception 類(lèi),添加了 _type 成員變量,
// 重寫(xiě)了 what() 函數(shù),返回 HTTP 服務(wù)器異常信息。
class HttpServerException : public Exception {
public:HttpServerException(const string& errmsg, int id, const string& type): Exception(errmsg, id), _type(type) {}virtual string what() const override {return "[HTTP " + _type + " Error] " + Exception::what();}private:string _type; // 請(qǐng)求類(lèi)型(GET/POST等)
};
what()
?函數(shù)的作用:what()
?函數(shù)是一個(gè)虛函數(shù),在基類(lèi)中定義,派生類(lèi)可以重寫(xiě)該函數(shù)。通過(guò)基類(lèi)指針或引用調(diào)用?what()
?函數(shù)時(shí),會(huì)根據(jù)實(shí)際對(duì)象的類(lèi)型調(diào)用相應(yīng)派生類(lèi)的?what()
?函數(shù),實(shí)現(xiàn)多態(tài)。這樣可以方便地根據(jù)不同的異常類(lèi)型輸出不同的異常信息。
2. 異常體系使用示例
// 模擬 SQL 管理函數(shù),可能拋出 SqlException
void SQLMgr() {srand(time(0));if (rand() % 7 == 0) {throw SqlException("權(quán)限不足", 100, "select * from name = '張三'");}std::cout << "執(zhí)行成功" << std::endl;
}// 模擬緩存管理函數(shù),可能拋出 CacheException 或調(diào)用 SQLMgr 拋出 SqlException
void CacheMgr() {srand(time(0));if (rand() % 5 == 0) {throw CacheException("權(quán)限不足", 100);} else if (rand() % 6 == 0) {throw CacheException("數(shù)據(jù)不存在", 101);}SQLMgr();
}// 模擬 HTTP 服務(wù)器函數(shù),可能拋出 HttpServerException 或調(diào)用 CacheMgr 拋出其他異常
void HttpServer() {srand(time(0));if (rand() % 3 == 0) {throw HttpServerException("請(qǐng)求資源不存在", 100, "get");} else if (rand() % 4 == 0) {throw HttpServerException("權(quán)限不足", 101, "post");}CacheMgr();
}// 主函數(shù),捕獲異常并處理
int main() {while (1) {Sleep(500);try {HttpServer();} catch (const Exception& e) { // 捕獲基類(lèi)異常對(duì)象,利用多態(tài)處理不同派生類(lèi)異常std::cout << e.what() << std::endl;} catch (...) {std::cout << "Unkown Exception" << std::endl;}}return 0;
}
3. 常用異常編號(hào)
編號(hào) | 異常描述 |
---|---|
1 | 沒(méi)有權(quán)限 |
2 | 服務(wù)器掛了 |
3 | 網(wǎng)絡(luò)錯(cuò)誤 |
七、常用標(biāo)準(zhǔn)庫(kù)異常
bad_alloc
:當(dāng)使用?new
?運(yùn)算符進(jìn)行動(dòng)態(tài)內(nèi)存分配失敗時(shí),會(huì)拋出?bad_alloc
?異常。out_of_range
:當(dāng)使用容器(如?std::vector
、std::string
?等)的成員函數(shù)訪(fǎng)問(wèn)越界元素時(shí),會(huì)拋出?out_of_range
?異常。invalid_argument
:當(dāng)函數(shù)接收到無(wú)效的參數(shù)時(shí),會(huì)拋出?invalid_argument
?異常。
八、異常的優(yōu)缺點(diǎn)
1. 優(yōu)點(diǎn)
- 清晰準(zhǔn)確的錯(cuò)誤信息:異常對(duì)象可以定義豐富的信息,相比錯(cuò)誤碼方式,能更清晰準(zhǔn)確地展示錯(cuò)誤信息,甚至可以包含堆棧調(diào)用信息,有助于更好地定位程序的 bug。
- 簡(jiǎn)化錯(cuò)誤處理流程:在函數(shù)調(diào)用鏈中,使用返回錯(cuò)誤碼的傳統(tǒng)方式需要層層返回錯(cuò)誤,最外層才能拿到錯(cuò)誤信息并處理。而異常體系中,不管是深層函數(shù)還是中間層函數(shù)出錯(cuò),拋出的異常會(huì)直接跳到合適的?
catch
?塊中,由調(diào)用者直接處理錯(cuò)誤。//示例代碼(錯(cuò)誤碼方式) #include <iostream> #include <errno.h>int ConnectSql() {// 用戶(hù)名密碼錯(cuò)誤if (...) {return 1;}// 權(quán)限不足if (...) {return 2;}return 0; }int ServerStart() {if (int ret = ConnectSql() < 0) {return ret;}int fd = socket();if (fd < 0) {return errno;}return 0; }int main() {if (ServerStart() < 0) {// 處理錯(cuò)誤}return 0; }
//示例代碼(異常方式) #include <iostream> #include <stdexcept>void ConnectSql() {// 用戶(hù)名密碼錯(cuò)誤if (...) {throw std::runtime_error("用戶(hù)名密碼錯(cuò)誤");}// 權(quán)限不足if (...) {throw std::runtime_error("權(quán)限不足");} }void ServerStart() {ConnectSql();int fd = socket();if (fd < 0) {throw std::system_error(errno, std::system_category(), "socket 錯(cuò)誤");} }int main() {try {ServerStart();} catch (const std::exception& e) {std::cout << "捕獲到異常: " << e.what() << std::endl;}return 0; }
- 與第三方庫(kù)兼容:很多第三方庫(kù)(如?
boost
、gtest
、gmock
?等)都使用了異常機(jī)制,使用這些庫(kù)時(shí),我們也需要使用異常來(lái)與之配合。 - 適合特定函數(shù):對(duì)于一些沒(méi)有返回值的函數(shù)(如構(gòu)造函數(shù)),或者不方便使用返回值表示錯(cuò)誤的函數(shù)(如?
T& operator[]
),使用異常處理錯(cuò)誤更加合適。
2. 缺點(diǎn)
- 執(zhí)行流混亂:異常會(huì)導(dǎo)致程序的執(zhí)行流亂跳,尤其是在運(yùn)行時(shí)出錯(cuò)拋異常時(shí),會(huì)使程序的控制流變得復(fù)雜,增加了跟蹤調(diào)試和分析程序的難度。
- 性能開(kāi)銷(xiāo):異常處理會(huì)有一定的性能開(kāi)銷(xiāo),不過(guò)在現(xiàn)代硬件速度較快的情況下,這個(gè)影響通??梢院雎圆挥?jì)。
- 異常安全問(wèn)題:C++ 沒(méi)有垃圾回收機(jī)制,資源需要自己管理。使用異常容易導(dǎo)致內(nèi)存泄漏、死鎖等異常安全問(wèn)題,需要使用 RAII(資源獲取即初始化)技術(shù)來(lái)管理資源,增加了學(xué)習(xí)成本。
- 標(biāo)準(zhǔn)庫(kù)異常體系混亂:C++ 標(biāo)準(zhǔn)庫(kù)的異常體系定義不夠完善,導(dǎo)致不同開(kāi)發(fā)者各自定義自己的異常體系,使得代碼的可維護(hù)性和兼容性受到影響。
- 異常規(guī)范問(wèn)題:異常需要規(guī)范使用,否則會(huì)給外層捕獲異常的用戶(hù)帶來(lái)困擾。異常規(guī)范主要包括兩點(diǎn):一是拋出的異常類(lèi)型都繼承自一個(gè)基類(lèi),二是函數(shù)是否拋異常、拋什么異常,都使用?
func() throw();
?的方式進(jìn)行規(guī)范化。
總結(jié)
異??傮w而言利大于弊,在工程中鼓勵(lì)使用異常。而且面向?qū)ο蟮木幊陶Z(yǔ)言基本都采用異常處理錯(cuò)誤,這也是軟件開(kāi)發(fā)的趨勢(shì)。
?