一、基礎(chǔ)
1. 異常的概念
異常是程序在運(yùn)行過程中出現(xiàn)非正常情況的處理機(jī)制。當(dāng)出現(xiàn)異常時(shí)程序會(huì)停止運(yùn)行并調(diào)用異常處理程序。
2. 異常的分類
異??梢苑譃閮?nèi)置異常和自定義異常
2.1 內(nèi)置異常
C++ 標(biāo)準(zhǔn)庫(kù)提供了許多預(yù)定義的異常類,稱為內(nèi)置異常,包括以下幾種:
-
std::exception
:所有標(biāo)準(zhǔn)異常類的基類。 -
std::logic_error
:表示程序邏輯錯(cuò)誤。 -
std::runtime_error
:表示運(yùn)行時(shí)錯(cuò)誤。
2.2 自定義異常
除了使用內(nèi)置異常,我們還可以創(chuàng)建自定義異常類來處理特定錯(cuò)誤。自定義異常類可以繼承自內(nèi)置異常類以實(shí)現(xiàn)異常的精細(xì)控制。
3. 異常的處理方式
C++ 提供了以下幾種處理異常的方式:
3.1 try-catch 語句
try {
// 可能會(huì)拋出異常的代碼塊
} catch (exception1_type exception1_var) {
// 處理異常類型1
} catch (exception2_type exception2_var) {
// 處理異常類型2
}
使用 try
代碼塊來包含可能拋出異常的代碼,緊跟 catch
塊來處理捕獲到的異常。當(dāng)拋出異常后,程序會(huì)自動(dòng)匹配 catch
塊中與對(duì)應(yīng)異常相同類型的代碼塊執(zhí)行,保證程序正常執(zhí)行。
3.2 throw 語句
throw exception_object;
使用 throw
語句拋出一個(gè)異常對(duì)象,使程序進(jìn)入異常處理模式。 exception_object
可以是基本類型或?qū)ο螅踔量梢允亲远x類型的實(shí)例。
3.3 noexcept 修飾符
void function_name() noexcept {
// 不會(huì)拋出異常的函數(shù)
}
noexcept 修飾符指示函數(shù)不拋出異常。使用 noexcept
可以優(yōu)化程序性能。當(dāng)程序遇到一個(gè)沒有 noexcept
修飾符的函數(shù),會(huì)假設(shè)這個(gè)函數(shù)可能拋出異常,導(dǎo)致額外的代碼執(zhí)行。
3.4 finally 語句塊
try{
// 可能拋出異常的代碼塊
} catch(...) {
// 處理異常
} finally {
// 無論有無異常都執(zhí)行
}
finally 語句在 try-catch 語句的末尾執(zhí)行,無論異常是否拋出。通常用于釋放資源等程序收尾工作。
綜上所述異常處理是程序設(shè)計(jì)中重要而必不可少的一部分。了解常見的異常處理方式,可以讓我們更加高效地處理程序中的錯(cuò)誤,并確保程序的正常運(yùn)行。
二、 異常處理機(jī)制
C++中的異常處理機(jī)制用于處理程序運(yùn)行過程中的異常情況。異常可以是程序中的一種錯(cuò)誤或者突發(fā)事件,可能會(huì)導(dǎo)致程序崩潰,但是正常情況下我們希望程序可以平穩(wěn)地運(yùn)行下去,去處理這些異常情況。在這樣的情況下, C++的異常處理機(jī)制發(fā)揮了巨大的作用。
1 try-catch 語句塊
try-catch 是處理 C++ 異常的關(guān)鍵工具。它的主要作用是將異常處理分離出代碼,將異常在運(yùn)行時(shí)捕獲并處理。
try {
// 可能引發(fā)異常的代碼塊
} catch (Type1 arg1) {
// 處理Type1類型的異常
} catch (Type2 arg2) {
// 處理Type2類型的異常
} catch (Type3 arg3) {
// 處理Type3類型的異常
}
當(dāng)一個(gè)異常被拋出的時(shí)候(通常是使用 throw 語句),與拋出異常的類型相匹配的 catch 塊會(huì)被調(diào)用,它會(huì)處理異常并從這個(gè)異常中恢復(fù)程序執(zhí)行。
2 異常處理流程
異常處理的整個(gè)過程是一種順序執(zhí)行模型,以下是它的基本流程:
- 程序執(zhí)行到可能拋出異常的代碼時(shí),這段代碼必須嵌入到 try 塊中。
- 如果在 try 塊中的代碼引發(fā)了異常,程序會(huì)跳轉(zhuǎn)到與拋出異常類型匹配的 catch 塊中,catch 塊負(fù)責(zé)處理異常。
- 最后程序會(huì)從 catch 塊中退出,向下執(zhí)行任何后續(xù)代碼。
簡(jiǎn)單來說,我們可以將異常處理機(jī)制的處理過程看作是程序運(yùn)行時(shí)一條流程線,它沿著 try-catch 塊執(zhí)行,遇到異常時(shí)跳轉(zhuǎn) catch 塊那里處理異常,最后退出 catch 塊。
3 標(biāo)準(zhǔn)異常類
C++ 標(biāo)準(zhǔn)庫(kù)中定義了一些異常類,這些異常類是所有 C++ 應(yīng)用程序都可以使用的。由于這些異常類是預(yù)定義的,因此它們都是放在 std 命名空間之下。
C++ 標(biāo)準(zhǔn)庫(kù)提供的異常類:
-
std::exception
—— 所有標(biāo)準(zhǔn)異常類的基類。 -
std::bad_alloc
—— 在申請(qǐng)內(nèi)存失敗時(shí)拋出。 -
std::bad_cast
—— 將一個(gè)指向派生類的基類指針強(qiáng)制轉(zhuǎn)換為派生類指針時(shí),如果類不是目標(biāo)類型,則拋出。 -
std::ios_base::failure
—— I/O 操作失敗時(shí)拋出。 -
std::out_of_range
—— 數(shù)組訪問越界時(shí)拋出。 -
std::invalid_argument
—— 提供無效參數(shù)時(shí)拋出。 -
std::length_error
—— 嘗試創(chuàng)建一個(gè)超出可分配內(nèi)存大小的vector、string等時(shí)發(fā)生。 -
std::logic_error
—— 人為 bug,如出現(xiàn)未在程序中考慮到的條件等,通常還有一些 derived class 總結(jié)不同的情況(此類異??梢栽诰幾g環(huán)境中靜態(tài)檢查出來)。 -
std::runtime_error
—— 運(yùn)行時(shí)錯(cuò)誤,一般情況下是在單個(gè)文件運(yùn)行時(shí)出錯(cuò)。(常常由文件、網(wǎng)絡(luò)等外部原因引起)
以上這些異常類是為了解決常見的情況而設(shè)計(jì)的,它們能夠支持很多常見的內(nèi)部錯(cuò)誤,如果我們想要精細(xì)控制異常,我們還可以創(chuàng)建自定義異常。
通過使用異常處理機(jī)制,程序員可以保證程序的正常運(yùn)行,在發(fā)生異常時(shí)捕獲并處理它們,從而防止程序崩潰。同時(shí)使用標(biāo)準(zhǔn)異常類,也可以避免一些常見的錯(cuò)誤和異常。
三、 拋出異常
在C++中可以使用異常處理機(jī)制來處理程序中的異常情況。當(dāng)程序出現(xiàn)錯(cuò)誤或突發(fā)事件時(shí),異常處理機(jī)制可以讓程序正常運(yùn)行下去,而不會(huì)導(dǎo)致程序崩潰。那么如何拋出異常呢,我們一起來看看
1 throw語句
使用throw語句可以拋出異常,throw語句必須跟上一個(gè)表達(dá)式該表達(dá)式是拋出的異常對(duì)象,如下所示:
throw someException; // 拋出異常
我們可以用任何類型的值做為異常對(duì)象,不過通常我們會(huì)把異常對(duì)象設(shè)置為標(biāo)準(zhǔn)庫(kù)的異常類之一
2 異常類型
異常類型是某種類型的值,它可以用于標(biāo)識(shí)需要通知程序時(shí)發(fā)生了什么錯(cuò)誤。異常類型可以是任意類型,但為了能夠與 catch 塊中的異常參數(shù)類型匹配,通常使用異常類的對(duì)象或指針。
下面是一個(gè)簡(jiǎn)單的示例,在函數(shù)中拋出一個(gè)異常:
// 聲明一個(gè)自定義的異常類
class MyException : public exception {
public:
// 重寫 what() 函數(shù),返回異常信息
const char* what() const noexcept override {
return "MyException occurred";
}
};
void myFunction() {
throw MyException(); // 拋出自定義異常
}
在拋出異常時(shí)可以使用任何類型的值,但是為了能夠被 catch 塊所匹配,一般使用異常類的對(duì)象或指針,也可以自定義異常類
3 異常傳遞
如果在函數(shù)中拋出了異常,那么異常會(huì)被拋到調(diào)用該函數(shù)的代碼中。如果這個(gè)函數(shù)也沒有捕獲這個(gè)異常,那么異常就會(huì)繼續(xù)傳遞到更高的層次,直到被捕獲為止。
下面是一個(gè)示例代碼,展示了異常是如何傳遞的:
void functionC() {
cout << "Starting function C" << endl;
throw MyException(); // 拋出自定義異常
cout << "Ending function C" << endl;
}
void functionB() {
cout << "Starting function B" << endl;
functionC(); // 調(diào)用functionC
cout << "Ending function B" << endl;
}
void functionA() {
cout << "Starting function A" << endl;
try {
functionB(); // 調(diào)用functionB
} catch (const exception& e) {
cerr << e.what() << endl; // 捕獲并處理異常
}
cout << "Ending function A" << endl;
}
int main() {
cout << "Starting main function" << endl;
functionA(); // 調(diào)用functionA
cout << "Ending main function" << endl;
return 0;
}
輸出結(jié)果為:
Starting main function
Starting function A
Starting function B
Starting function C
MyException occurred
Ending function A
Ending main function
首先main函數(shù)調(diào)用 functionA。functionA 內(nèi)部調(diào)用 functionB,并且包含一個(gè) try 塊,用來捕獲異常。functionB 內(nèi)部調(diào)用 functionC并拋出了一個(gè)異常,這個(gè)異常傳遞到了 functionA 中被 catch 塊捕獲并處理。
這里需要注意的是一旦拋出了一個(gè)異常,函數(shù)就會(huì)終止。因此functionC 中的 cout 語句不會(huì)被執(zhí)行到。
總結(jié)起來異常處理機(jī)制是 C++ 中重要的一部分,它可以幫助程序處理錯(cuò)誤和突發(fā)事件,并保證程序的正常運(yùn)行。在拋出異常時(shí)需要考慮使用什么類型的異常對(duì)象,并養(yǎng)成良好的習(xí)慣在適當(dāng)?shù)牡胤讲东@異常。
四、 捕獲異常
在C++中捕獲異常是指捕獲并處理由 throw 語句拋出的異常。在異常處理機(jī)制中,try 塊用于捕獲異常并處理它們,而 catch 塊則用于處理 try 塊中拋出的異常。下面我們來看看如何使用 catch 塊來捕獲和處理異常
1 catch語句
catch 塊用于捕獲和處理由 try 塊拋出的異常。catch 塊必須跟上一個(gè)括號(hào),括號(hào)中是一個(gè)參數(shù),它是 catch 塊的異常參數(shù),用來接收拋出的異常對(duì)象
如下所示:
try {
// 可能會(huì)發(fā)生異常的代碼
} catch (exceptionType& e) {
// 處理異常的代碼
}
這里的 exceptionType 是異常類型的名字,使用與 throw 語句中拋出的一致的類型或基類類型。&e 表示將異常對(duì)象的引用傳遞給 catch 塊中的代碼,從而使得 catch 塊能夠訪問并處理該異常對(duì)象。需要注意的是,&e 表示對(duì)異常對(duì)象的只讀訪問不能對(duì)傳遞過來的異常對(duì)象進(jìn)行修改。
我們可以使用多個(gè) catch 塊來處理不同類型的異常,這樣可以將異常處理代碼與常規(guī)代碼分離,更好地管理程序錯(cuò)誤和異常。下面我們來看看多個(gè) catch 塊的順序和匹配規(guī)則
2 多個(gè)catch語句的順序與匹配規(guī)則
當(dāng)由 try 塊拋出一個(gè)異常時(shí),程序會(huì)按照由上到下的順序遍歷 catch 塊,直到找到一個(gè)與拋出的異常匹配的 catch 塊為止。這里的匹配指的是異常參數(shù)類型與拋出的異常對(duì)象類型一致,或者是該異常的基類類型。如果找不到合適的 catch 塊,則程序?qū)惓鬟f到更高層次的代碼中。
需要注意的是如果有多個(gè) catch 塊可以匹配拋出的異常,C++ 編譯器將使用第一個(gè)合適的 catch 塊進(jìn)行處理,而不是使用最具體的 catch 塊。因此在編寫多個(gè) catch 塊時(shí),應(yīng)該將最具體的 catch 塊放在最前面
下面是一個(gè)示例,它展示了多個(gè) catch 塊的順序和匹配規(guī)則:
try {
// 可能會(huì)發(fā)生異常的代碼
} catch (const Exception1& e) {
// 處理異常1的代碼
} catch (const Exception2& e) {
// 處理異常2的代碼
} catch (const Exception3& e) {
// 處理異常3的代碼
} catch (const exception& e) {
// 處理其他異常的代碼
}
在這個(gè)示例中當(dāng)由 try 塊拋出 Exception1 類型的異常時(shí),程序會(huì)執(zhí)行第一個(gè) catch 塊。當(dāng)拋出 Exception2 類型的異常時(shí),程序會(huì)執(zhí)行第二個(gè) catch 塊。當(dāng)拋出 Exception3 類型的異常時(shí),程序會(huì)執(zhí)行第三個(gè) catch 塊。當(dāng)拋出其他異常時(shí)程序會(huì)執(zhí)行最后一個(gè) catch 塊。
3 異常處理機(jī)制的使用示例
下面是一個(gè)簡(jiǎn)單的示例,它展示了異常處理機(jī)制的使用。我們自定義一個(gè)異常類 DivideByZeroException用于拋出在除法運(yùn)算中除數(shù)為零的異常。在該程序中,我們使用 try 塊和 catch 塊來捕獲和處理異常,并且為了更好地管理程序錯(cuò)誤和異常,將業(yè)務(wù)邏輯和異常處理代碼分離開來。
#include <iostream>
#include <exception>
using namespace std;
// 自定義一個(gè)異常類
class DivideByZeroException : public exception {
public:
// 重寫 what() 函數(shù),返回異常信息
const char* what() const noexcept override {
return "Attempt to divide by zero";
}
};
// 除法函數(shù),在該函數(shù)中可能會(huì)拋出一個(gè) DivideByZeroException 異常
double divide(double a, double b) {
if (b == 0) {
throw DivideByZeroException();
}
return a / b;
}
int main() {
double a = 42, b = 0, result;
try {
result = divide(a, b);
cout << "Result is: " << result << endl;
} catch (const DivideByZeroException& e) {
cerr << "Exception caught: " << e.what() << endl;
}
cout << "Program continues to run" << endl;
return 0;
}
在該示例中定義了一個(gè) 除法函數(shù) divide,該函數(shù)接收兩個(gè) double 類型的參數(shù) a 和 b,如果除數(shù) b 為零,則拋出一個(gè) DivideByZeroException 異常。在程序中我們?cè)?try 塊中調(diào)用 divide 函數(shù),并通過 catch 塊來捕獲并處理拋出的異常。我們?cè)?catch 塊中打印出異常信息并繼續(xù)執(zhí)行程序。最后輸出的結(jié)果應(yīng)該是:
Exception caught: Attempt to divide by zero
Program continues to run
以上就是關(guān)于 catch 塊的使用和異常處理機(jī)制的一個(gè)簡(jiǎn)單示例,希望可以幫助您更好地掌握 C++ 中的異常處理機(jī)制。
五、 C++11 新增異常功能
在C++11中增加了一些異常的功能,讓我們來看看
1 noexcept操作符
noexcept是一個(gè)C++11新增的操作符,它用于指明一個(gè)函數(shù)是否可能拋出異常。當(dāng)一個(gè)函數(shù)有可能拋出異常時(shí),我們可以在其聲明或定義前使用 noexcept 關(guān)鍵字告知編譯器這一信息。這個(gè)操作符的作用是幫助編譯器進(jìn)行優(yōu)化,提高代碼的效率。
一個(gè)使用 noexcept 的函數(shù)可以被認(rèn)為是“拋出異常不影響程序正確性”的,這意味著該函數(shù)不會(huì)拋出異常,或者拋出異常后程序可以優(yōu)雅地進(jìn)行終止。
下面是一個(gè)使用 noexcept 關(guān)鍵字的示例:
#include <iostream>
using namespace std;
void func1() noexcept {
cout << "func1: No exceptions here!" << endl;
}
void func2() {
throw runtime_error("Exception in func2");
}
int main() {
cout << boolalpha;
cout << "func1 is noexcept: " << noexcept(func1()) << endl; // 輸出 true
cout << "func2 is noexcept: " << noexcept(func2()) << endl; // 輸出 false
}
在這個(gè)示例中定義了兩個(gè)函數(shù) func1 和 func2,其中 func1 使用了 noexcept,并且沒有拋出任何異常。而 func2 則可能會(huì)拋出一個(gè) runtime_error 異常。在主函數(shù)中,我們使用 noexcept 來檢查 func1 和 func2 是否使用了 noexcept 進(jìn)行聲明或定義,從而判斷它們是否可能會(huì)拋出異常。
需要注意的是noexcept 操作符只能用于函數(shù)聲明或定義的末尾,這個(gè)末尾必須是分號(hào)或花括號(hào)。對(duì)于類的成員函數(shù),可以在函數(shù)名后的括號(hào)內(nèi)使用它,如下所示:
class MyClass {
public:
void func1() noexcept;
void func2() noexcept(true) { // 或者把true換成false
throw runtime_error("Exception in func2");
}
};
在類的成員函數(shù)聲明或定義中也可以使用 noexcept 進(jìn)行限定,但有一個(gè)細(xì)微的差別,可以將其放在括號(hào)內(nèi)來表示類作為一個(gè)新環(huán)境。括號(hào)內(nèi)的布爾值指定了該函數(shù)是否可能拋出異常。
2 異常列表(function try-block)
在 C++11 中還新增了異常列表,也叫函數(shù) try 塊。這個(gè)功能允許我們?cè)诤瘮?shù)定義中捕獲異常并處理,與在函數(shù)體中進(jìn)行異常處理不同,這讓我們可以將所有異常處理代碼放在同一個(gè)位置,提高代碼的可讀性和可維護(hù)性。
在函數(shù)體開始之前,在函數(shù)參數(shù)列表后立即使用 try 關(guān)鍵字并緊跟著花括號(hào),在花括號(hào)中包含函數(shù)體,需要同時(shí)聲明和捕獲異常。需要注意的是,如果一個(gè)函數(shù)既有函數(shù) try 塊,又有普通的 try-catch 塊,它們的處理順序是不一樣的,函數(shù) try 塊中的異常處理將先于普通的 try-catch 塊執(zhí)行。
下面是一個(gè)函數(shù) try 塊的示例代碼:
#include <iostream>
#include <stdexcept>
using namespace std;
class MyClass {
public:
MyClass() : num(42) {
cout << "Constructing MyClass" << endl;
}
~MyClass() noexcept(false) {
cout << "Destructing MyClass" << endl;
throw runtime_error("Exception in destructor");
}
void DoSomething() noexcept(false) {
cout << "Doing something" << endl;
throw runtime_error("Exception in DoSomething");
}
private:
int num;
};
void f(MyClass& mc) try {
mc.DoSomething();
} catch (const exception& e) {
cerr << "Caught exception in f(): " << e.what() << endl;
}
int main() {
MyClass mc;
f(mc);
return 0;
}
在這個(gè)示例中定義了一個(gè) MyClass 類,該類包含了一個(gè)有副作用的默認(rèn)構(gòu)造函數(shù)和一個(gè)有副作用的析構(gòu)函數(shù),它們都可能會(huì)拋出異常。MyClass 類還包含一個(gè)成員函數(shù) DoSomething,這個(gè)函數(shù)也可能會(huì)拋出異常。我們使用了函數(shù) try 塊來定義了一個(gè)函數(shù) f,該函數(shù)調(diào)用了成員函數(shù) DoSomething,并在函數(shù)名后面使用了 try 來聲明和捕獲異常。在 catch 塊中打印出異常信息。
在主函數(shù)中創(chuàng)建了一個(gè) MyClass 對(duì)象,然后調(diào)用函數(shù) f 來演示函數(shù) try 塊的使用。
以上就是 C++11 中新增的兩個(gè)異常功能:noexcept 操作符和異常列表(function try-block)。這些新功能使我們可以更好地處理異常,寫出更健壯、高效的代碼。
六、常見錯(cuò)誤和異常處理
在編寫代碼時(shí),我們難免會(huì)遇到一些錯(cuò)誤與異常。這些錯(cuò)誤與異??赡軙?huì)導(dǎo)致我們的程序崩潰或者出現(xiàn)一些意想不到的行為。因此在編寫代碼的過程中,我們應(yīng)該注意一些常見的錯(cuò)誤和異常,以便及時(shí)解決它們或者避免它們的發(fā)生。
1 空指針異常
指針是我們編程過程中經(jīng)常使用的一個(gè)概念,空指針異常指當(dāng)我們使用一個(gè)空指針時(shí),會(huì)出現(xiàn)意想不到的行為或意外的程序崩潰。
下面是一個(gè)關(guān)于空指針異常的代碼示例:
int* p = nullptr; // 定義一個(gè)空指針
int a = *p; // 這里會(huì)發(fā)生空指針異常
在代碼示例中定義了一個(gè)空指針 p
,然后試圖去訪問 p
指向的內(nèi)存空間中的值,并將其賦值給變量 a
,這里就會(huì)發(fā)生空指針異常。
在C++ 中我們可以通過以下方式來避免空指針異常的發(fā)生:
- 在使用指針之前要進(jìn)行判斷,確保指針不為空。
- 在給指針分配內(nèi)存空間時(shí),要使用 new 操作符,并進(jìn)行異常處理。
2 內(nèi)存泄漏異常
內(nèi)存泄漏指程序在分配了內(nèi)存空間后,沒有合適的方式來釋放它。內(nèi)存泄漏會(huì)導(dǎo)致程序內(nèi)存空間的消耗過大,從而影響程序性能。
下面是一個(gè)內(nèi)存泄漏的代碼示例:
int* p = new int; // 分配了一個(gè)動(dòng)態(tài)內(nèi)存空間
p = nullptr; // 將指針指向空
在代碼示例中通過 new 操作符動(dòng)態(tài)地分配了一段內(nèi)存空間,但是在之后將指針置為空時(shí),我們并沒有使用 delete 來釋放所分配的內(nèi)存空間,從而導(dǎo)致了內(nèi)存泄漏。
在C++ 中應(yīng)該避免內(nèi)存泄漏的發(fā)生。一種常見的做法是,在分配內(nèi)存空間時(shí)使用智能指針(smart pointer),它們會(huì)在指針不再需要時(shí)自動(dòng)釋放所分配的內(nèi)存空間。
3 數(shù)組越界異常
數(shù)組越界是指訪問數(shù)組的時(shí)候,訪問一個(gè)超出了數(shù)組范圍的索引,其結(jié)果是未定義的行為。訪問越界的錯(cuò)誤有時(shí)可能看起來沒有任何影響,但是在某些情況下,它們可能會(huì)對(duì)程序的運(yùn)行時(shí)行為產(chǎn)生嚴(yán)重的影響。
下面是一個(gè)數(shù)組越界的代碼示例:
int arr[5] = {1, 2, 3, 4, 5};
int n = 6;
int a = arr[n]; // 這里會(huì)發(fā)生數(shù)組越界
在代碼示例中定義了一個(gè)長(zhǎng)度為 5 的數(shù)組 arr
,然后試圖訪問數(shù)組中不存在的索引 n
,這里就會(huì)發(fā)生數(shù)組越界異常。
在C++ 中我們可以通過以下方式來避免數(shù)組越界異常的發(fā)生:
- 在訪問數(shù)組元素時(shí),要確保訪問的索引在有效范圍內(nèi)。
- 在使用數(shù)組之前要進(jìn)行初始化。
4 死鎖異常
死鎖是指兩個(gè)或多個(gè)進(jìn)程(線程)相互等待對(duì)方釋放共享資源,從而導(dǎo)致進(jìn)程(線程)阻塞的情況。死鎖是多線程編程中比較常見的一個(gè)問題,一旦發(fā)生,會(huì)導(dǎo)致程序的掛起,從而影響程序的性能。
下面是一個(gè)死鎖的代碼示例:
#include <mutex>
#include <thread>
void func(std::mutex& m1, std::mutex& m2) {
m1.lock();
m2.lock(); // 這里會(huì)導(dǎo)致死鎖
// ...
m2.unlock();
m1.unlock();
}
int main() {
std::mutex m1, m2;
std::thread t1(func, std::ref(m1), std::ref(m2));
std::thread t2(func, std::ref(m2), std::ref(m1));
t1.join();
t2.join();
return 0;
}
在代碼示例中定義了兩個(gè)線程 t1
和 t2
,它們分別執(zhí)行函數(shù) func
。在函數(shù)中,我們使用了兩個(gè)互斥量 m1
和 m2
來保證在訪問共享資源前線程的同步,但是在執(zhí)行線程 t1
和 t2
的時(shí)候,如果它們同時(shí)試圖獲取 m1
和 m2
的互斥鎖,就會(huì)導(dǎo)致死鎖異常的發(fā)生。文章來源:http://www.zghlxwxcb.cn/news/detail-799537.html
在 C++ 中可以通過以下方式來避免死鎖異常的發(fā)生:文章來源地址http://www.zghlxwxcb.cn/news/detail-799537.html
- 避免使用多個(gè)互斥量進(jìn)行同步。
- 在使用互斥量時(shí),謹(jǐn)慎使用 lock 和 unlock
- 使用 RAII 實(shí)現(xiàn)自動(dòng)加鎖和解鎖,在 C++11 中,我們可以使用
std::lock_guard
和std::unique_lock
來實(shí)現(xiàn)在構(gòu)造函數(shù)中加鎖,在析構(gòu)函數(shù)中解鎖的操作
到了這里,關(guān)于深度解析C++異常處理機(jī)制:分類、處理方式、常見錯(cuò)誤及11新增功能的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!