亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

C++中異常的深度解析

 更新時(shí)間:2025年03月27日 09:34:42   作者:源博不太 “ 淵博 ”  
異常處理機(jī)制允許程序中獨(dú)立開發(fā)部分能夠在運(yùn)行時(shí)就出現(xiàn)的問題進(jìn)行通信并做出相應(yīng)的處理,這篇文章主要介紹了C++中異常的深度解析,需要的朋友可以參考下

1 異常的概念及使用

1.1 異常的概念

1>.異常處理機(jī)制允許程序中獨(dú)立開發(fā)部分能夠在運(yùn)行時(shí)就出現(xiàn)的問題進(jìn)行通信并做出相應(yīng)的處理,異常使得我們能夠?qū)栴}的檢測(cè)與解決問題的過程分開,程序的一部分負(fù)責(zé)檢測(cè)問題的出現(xiàn),然后解決問題的任務(wù)傳遞給出現(xiàn)的另一部分,檢測(cè)環(huán)節(jié)無須知道問題的處理模塊的所有細(xì)節(jié)。

2>.C語(yǔ)言主要是通過錯(cuò)誤碼的形式處理錯(cuò)誤,錯(cuò)誤碼本質(zhì)上就是對(duì)錯(cuò)誤信息進(jìn)行分類編號(hào),拿到錯(cuò)誤碼以后還需要我們自己去查詢錯(cuò)誤信息,比較麻煩。異常時(shí)會(huì)拋出一個(gè)對(duì)象,這個(gè)對(duì)象可以涵蓋更全面的各種信息。

1.2 異常的拋出和捕獲

1>.程序出現(xiàn)問題時(shí),我們通過拋出(throw)一個(gè)對(duì)象來引發(fā)一個(gè)異常,該對(duì)象的類型以及當(dāng)前的調(diào)用鏈決定了改由哪個(gè)catch的處理代碼來處理異常。

2>.被選中的處理代碼是調(diào)用鏈中與該類型匹配且離拋出異常的位置最近的那一個(gè)catch的處理代碼。根據(jù)拋出對(duì)象的類型與內(nèi)容,程序的拋出異常部分要告知異常處理部分到底發(fā)生了什么錯(cuò)誤。

3>.當(dāng)throw執(zhí)行時(shí),throw后面的語(yǔ)句將不再被執(zhí)行。程序的執(zhí)行從throw位置會(huì)跳到與之匹配的catch模塊,catch可能是同一個(gè)函數(shù)中的一個(gè)局部catch模塊,也可能是調(diào)用鏈中的另一個(gè)函數(shù)中的catch模塊,控制權(quán)從throw位置轉(zhuǎn)移到了catch模塊的位置。這里還有兩個(gè)重要的含義:1.沿著調(diào)用鏈的函數(shù)可能會(huì)提早推出;2.一旦程序開始執(zhí)行異常處理程序,沿著調(diào)用鏈創(chuàng)建的對(duì)象都將會(huì)自動(dòng)被編譯器銷毀。

4>.拋出異常對(duì)象后,會(huì)生成一個(gè)異常對(duì)象的拷貝,因?yàn)閽伋龅漠惓?duì)象可能是一個(gè)局部對(duì)象,所以會(huì)生成一個(gè)拷貝對(duì)象,這個(gè)拷貝的對(duì)象會(huì)在catch模塊結(jié)束后就被銷毀了。(這里的處理類似于函數(shù)的傳值返回)

5>.在C++的異常處理過程中,我們常常選擇使用try-catch去處理異常,我們這里就先來講解一下這個(gè)try-catch:1.try:表示將有可能出現(xiàn)異常的代碼書寫在try代碼塊中;2.catch:try不能單獨(dú)使用,必須結(jié)合catch / finally / catch-finally(這里try結(jié)合catch),catch也不能單獨(dú)使用,必須結(jié)合try一起用。

int Divide(int a, int b)
{
	try
	{
		if (b == 0)//如果b等于0,就拋異常。
		{
			string s("Divide by zero condition!");
			throw s;//這里會(huì)將類型為string的對(duì)象s拋出去,去找這條調(diào)用鏈中與s這個(gè)對(duì)象類型匹配且離拋出異常的哪個(gè)位置的那一個(gè)catch代碼塊(拋出的并不是s對(duì)象,而是s這個(gè)異常對(duì)象的一個(gè)拷貝對(duì)象)。
		}
		else
		{
			return a / b;
		}
	}
	catch (int errid)//catch這個(gè)代碼塊接收的是int類型的一個(gè)對(duì)象。
	{
		cout << errid << endl;
	}
}
void Func(int a, int b)
{
	try
	{
		cout << Divide(a, b) << endl;
	}
	catch (const char* errmsg)
	{
		cout << errmsg << endl;
	}
}
int main()
{
	int a = 0, b = 0;
	cin >> a >> b;
	try
	{
		Func(a, b);
	}
	catch (const string* errmsg)//catch接收的是一個(gè)string類型的對(duì)象。
	{
	    cout << errmsg << endl;
    }
	return 0;
}//我們開始運(yùn)行程序,輸入兩個(gè)變量分別為10和0,在main函數(shù)中進(jìn)入try代碼塊中,去調(diào)用Func這個(gè)函數(shù),進(jìn)入Func這個(gè)局部棧幀中,又進(jìn)入到try代碼中,再去調(diào)用Divide函數(shù),首先,又去進(jìn)入到try代碼塊中,由于b==0,會(huì)進(jìn)入到if語(yǔ)句中去執(zhí)行代碼,通過throw來將s對(duì)象跑出來引發(fā)異常,編譯器這里會(huì)順著調(diào)用鏈去找接收string類型對(duì)象的catch模塊,首先找到第15這行代碼的catch模塊(因?yàn)殡xthrow的位置最近),類型不符合,再順著調(diào)用鏈去找,找到第26這行代碼的catch模塊,類型又不符合,再找到第39這行代碼的catch模塊,OK了,類型符合,就是errmsg這個(gè)對(duì)象接收到了Divide函數(shù)中跑出來的哪個(gè)對(duì)象s,既然是第39這行代碼的catch模塊接收了,那么程序的執(zhí)行就從拋出的位置跳到了第39這行代碼的catch模塊這里來了。
//當(dāng)我們?cè)贒ivide函數(shù)中拋出s對(duì)象時(shí),那么第7這句代碼之后的語(yǔ)句將不會(huì)再被執(zhí)行(僅限于Divide這個(gè)棧幀中),而且Func函數(shù)在這里其實(shí)是提早退出了的,F(xiàn)unc這個(gè)函數(shù)中,如果在調(diào)用了這個(gè)函數(shù)之前多余開辟了空間的話,那么編譯器在這里會(huì)自動(dòng)地將Func函數(shù)中開辟的那塊空間給銷毀掉。

上述函數(shù)的調(diào)用鏈:

1.3 棧展開

1>.拋出異常后,程序暫停當(dāng)前函數(shù)的執(zhí)行,開始尋找與之匹配的catch子句,首先檢查throw本身是否在try模塊的內(nèi)部,如果在的話則查找匹配的那個(gè)catch模塊,如果有匹配的,則跳到那個(gè)與之匹配的catch模塊的那個(gè)地方去進(jìn)行處理。

2>.如果當(dāng)前所在的這個(gè)函數(shù)中沒有try/catch,或者有try/catch子句但是類型不匹配,則退出當(dāng)前函數(shù),進(jìn)行在外層調(diào)用函數(shù)鏈中去查找,上述查找的catch模塊的過程被稱之為是棧展開。

3>.如果我們到達(dá)main函數(shù)的棧幀,并且依舊沒有找到與之匹配的catch模塊,那么程序在這里會(huì)自動(dòng)去調(diào)用標(biāo)準(zhǔn)庫(kù)中的terminate這個(gè)函數(shù)去終止程序,簡(jiǎn)單來說就是報(bào)錯(cuò)。

4>.如果找到匹配的catch模塊去處理后,catch模塊中的以及后續(xù)的代碼則會(huì)進(jìn)行執(zhí)行。

上圖就是一個(gè)棧展開的過程。

1.4 查找匹配的處理代碼

1>.一般情況下拋儲(chǔ)對(duì)象和catch接收的那個(gè)對(duì)象的類型是完全匹配的,如果有多個(gè)類型匹配的catch子句,那么就選擇離他位置更近的那個(gè)catch子句。

2>.但是也有一些例外,允許從非常量向常量的類型準(zhǔn)換,也就是權(quán)限縮小;允許數(shù)組轉(zhuǎn)換成指向數(shù)組元素類型的指針,函數(shù)被轉(zhuǎn)換成指向函數(shù)的指針;允許從派生類向基類類型的轉(zhuǎn)換,這一點(diǎn)非常實(shí)用,實(shí)際中繼承體系基本都是用這個(gè)方式去設(shè)計(jì)的。

3>.如果到main函數(shù)中,異常人就沒有被匹配的話就會(huì)被終止程序,不是發(fā)生嚴(yán)重錯(cuò)誤的情況下,我們是不期望程序最終的,所以一般的main函數(shù)中在最后都會(huì)使用catch(...),它可以捕獲任意類型的異常,但是我們是不知道異常的錯(cuò)誤是什么。注:一個(gè)try模塊我們可以搭配多個(gè)catch模塊。

//由于時(shí)間等等各種原因,我們這里就不一一為大家展示匹配的過程代碼了,我們接下來就來模擬設(shè)計(jì)一個(gè)繼承的匹配機(jī)制。
class person
{
public:
	person(const string& name)
		:_name(name)
	{
	}
protected:
	string _name;
};
class student :public person
{
public:
	student(const string& name, int id)
		:person(name)
		, _id(id)
	{
	}
private:
	int _id;
};
class teacher :public person
{
public:
	teacher(const string& name, int teach)
		:person(name)
		, _teach(teach)
	{
	}
private:
	int _teach;
};
void Print()
{
	if (rand() % 5 == 0)
	{
		throw student("學(xué)號(hào)", 20);
	}
	else if (rand() % 2 == 0)
	{
		throw teacher("工號(hào)", 32);
	}
	else
	{
		throw string();
	}
}
int main()
{
	try
	{
		Print();
	}
	catch (const person& p)
	{ }//可以捕捉所有繼承了person類型的對(duì)象。
	catch (...)//可以捕捉任意類型的異常對(duì)象。
	{ }
	return 0;
}//好了,我們這里直接來看Print函數(shù)中拋異常的操作,首先看第36到39這段代碼,它拋出的student類型的對(duì)象,在第55到56這段代碼中的catch子句被捕獲了,派生類的對(duì)象被基類類型的對(duì)象給捕獲了;再來看第40到43這段代碼,它拋出的是一個(gè)teacher類型的對(duì)象,在第55到56這段代碼中的catch子句被捕獲了,teacher這個(gè)派生類對(duì)象被person這個(gè)基類對(duì)象給捕獲了;最后看第44到47這段代碼,它所拋出的是一個(gè)string類型的對(duì)象,是被第57到58這段代碼中的catch子句捕獲的,第55到56這段代碼中的catch子句它主要捕獲的是person類型的對(duì)象以及繼承了person類的派生類對(duì)象,string類型與其不匹配,第55到56這段代碼中的catch子句捕獲不到,而第57到58這段代碼中的catch子句可以捕捉到任意類型的異常對(duì)象,因此就被第57到58這段代碼中的catch子句給捕捉到了。

1.5 異常重新拋出

1>.有時(shí)catch到一個(gè)異常對(duì)象后,需要對(duì)錯(cuò)誤進(jìn)行分類,其中的某種異常錯(cuò)誤需要進(jìn)行特殊的處理,其他錯(cuò)誤則重新拋出異常給外層調(diào)用鏈處理。捕獲異常需要重新拋出,直接throw;就可以把捕捉到的對(duì)象再次拋出。

void Print()
{
	int a = rand() % 2;
	try
	{
		throw string();
	}
	catch (string& s)
	{
		if (a == 1)
		{
			throw;//如果a==1的話,就將捕獲到的那個(gè)string類型的對(duì)象再次拋出。
		}
		else
		{
			cout << s << endl;
		}
	}
}
int main()
{
	try
	{
		Print();
	}
	catch (string& s)//Print函數(shù)將捕捉到的那個(gè)對(duì)象重新拋出后,被這個(gè)catch子句重新捕捉到了。
	{
		cout << s << endl;
	}
	return 0;
}

1.6 異常安全問題

1>.異常拋出后,后面的代碼就不再執(zhí)行了,前面申請(qǐng)了資源(內(nèi)存、鎖等),后面要進(jìn)行釋放(這里指的是我們自己用new/malloc向內(nèi)存申請(qǐng)的一塊資源,它在釋放時(shí)需要我們自己去調(diào)用delete函數(shù)),但是中間可能會(huì)拋異常就會(huì)導(dǎo)致資源沒有釋放,這里由于異常就引發(fā)了資源泄露,會(huì)產(chǎn)生安全性的問題。為了解決這個(gè)問題,那么我們就要在拋出到外層調(diào)用鏈之前要提前捕獲到這個(gè)異常對(duì)象,將那些資源釋放之后再將其重新拋出。當(dāng)然我們下一章要講解的智能指針章節(jié)中所講的RALL方式解決這種問題時(shí)更好的。

2>.其次在析構(gòu)函數(shù)中,如果在析構(gòu)函數(shù)的過程中拋出了異常的話,那么就也需要慎重處理(在C類語(yǔ)言中,只要是開創(chuàng)資源的函數(shù),如new、malloc或釋放資源的函數(shù),如free、delete,這幾個(gè)函數(shù)都有可能會(huì)拋異常),比如析構(gòu)函數(shù)要釋放10個(gè)資源,在釋放到第5個(gè)時(shí)拋出異常,則也需要捕獲處理,否則的話后面的5個(gè)資源就沒有釋放,也會(huì)造成資源泄露。

void Print()
{
	int* array = new int[10] {0};//創(chuàng)建一個(gè)int類型的數(shù)組空間,數(shù)組的對(duì)象為10。
	try
	{
		string s;
		throw s;//拋出一個(gè)string類型的對(duì)象。
	}
	catch (...)//我們?cè)趻伋霎惓?duì)象之前就申請(qǐng)了一塊有10個(gè)int類型空間大小的資源,為了防止出現(xiàn)資源泄露的問題,異常,我們需要Print函數(shù)內(nèi)部就捕獲到了這個(gè)異常對(duì)象,等將array執(zhí)行的那塊資源說服力之后,再將捕獲到的那個(gè)異常對(duì)象重新拋出即可。
	{
		delete[] array;
		throw;//將捕獲的那個(gè)對(duì)象重新拋出。
	}
	delete[] array;//如果這里并不會(huì)拋異常的話,編譯器不會(huì)走catch子句,異常這里還需再寫上一句刪除array指向的那塊資源的代碼。
}

1.7 異常規(guī)范

1>.對(duì)于用戶和編譯器而言,預(yù)先知道某個(gè)程序會(huì)不會(huì)拋出異常大有益處,知道某個(gè)函數(shù)是否會(huì)拋出異常會(huì)有助于簡(jiǎn)化調(diào)用函數(shù)的代碼。

2>.C++98中函數(shù)參數(shù)列表的后面接throw(),表示該函數(shù)不會(huì)拋異常,函數(shù)參數(shù)列表的后面接throw(類型1,類型2,...)表示可能會(huì)拋出多種類型的異常,將可能會(huì)拋出的類型之間均用逗號(hào)分割。

3>.C++98的這種方式有點(diǎn)過于復(fù)雜,在實(shí)踐中其實(shí)并不好用,C++11中對(duì)其進(jìn)行了簡(jiǎn)化,函數(shù)參數(shù)列表后面若加noexcept這個(gè)關(guān)鍵字就表示該函數(shù)不會(huì)拋異常,若啥都不加的話則表示可能會(huì)拋出異常。

4>.編譯器并不會(huì)在編譯時(shí)去檢查noexcept修飾了,也就是說如果一個(gè)函數(shù)用noexcept修飾了,但是同時(shí)又包含了throw語(yǔ)句或者調(diào)用的函數(shù)可能會(huì)拋出異常,編譯器還是會(huì)順利通過的(有些編譯器可能會(huì)報(bào)個(gè)警告)。但是如果一個(gè)聲明了noexcept的函數(shù)拋出了異常的話,程序便會(huì)去調(diào)用terminate終止程序。

5>.noexcept(expression)還可以作為一個(gè)運(yùn)算符去檢測(cè)一個(gè)表達(dá)式是否會(huì)拋出異常,可能會(huì)拋出異常的話則返回false,不會(huì)的話就會(huì)返回true。

void Print()noexcept
{
	int a = 0;
	cin >> a;
	if (a == 10)
	{
		throw "a==10";
	}
}
int main()
{
	try
	{
		Print();
	}
	catch (char* errmsg)
	{
		cout << errmsg << endl;
	}
	return 0;
}//如果我們大家仔細(xì)看上述這段代碼時(shí),稍微有一點(diǎn)問題,Print函數(shù)有拋出異常的風(fēng)險(xiǎn),但是Print函數(shù)的參數(shù)后面加了noexcept這個(gè)關(guān)鍵字,理論上來說的話是不能加這個(gè)關(guān)鍵字的,通過前面的解析,我們可知,這種情況下有的編譯器是不會(huì)報(bào)錯(cuò)的。我們現(xiàn)在來運(yùn)行這個(gè)代碼來看一下,如果我們輸入5的話,編譯器確實(shí)不會(huì)報(bào)錯(cuò),而且還完整地運(yùn)行了下來,但如果我們輸入10的話,程序在這里就別破中止運(yùn)行了,原因是因?yàn)镻rint這個(gè)用noexcept修飾的函數(shù)在運(yùn)行時(shí)拋出了一個(gè)異常對(duì)象。

2 標(biāo)準(zhǔn)庫(kù)的異常

1>.C++標(biāo)準(zhǔn)庫(kù)也定義了一套自己的異常繼承體系,基類是exception;所以我們?nèi)粘T趯懗绦驎r(shí),需要在主函數(shù)捕獲exception即可,要獲取異常信息,調(diào)用what函數(shù),what函數(shù)是一個(gè)虛函數(shù),派生類可以重寫。

OK,今天我們就先講到這里了,那么,我們下一篇再見,謝謝大家的支持!

到此這篇關(guān)于C++中異常的深度解析的文章就介紹到這了,更多相關(guān)C++異常內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • C++中的std::funture和std::promise實(shí)例詳解

    C++中的std::funture和std::promise實(shí)例詳解

    在線程池中獲取線程執(zhí)行函數(shù)的返回值時(shí),通常使用 std::future 而不是 std::promise 來傳遞返回值,這篇文章主要介紹了C++中的std::funture和std::promise實(shí)例詳解,需要的朋友可以參考下
    2024-05-05
  • C++實(shí)現(xiàn)基于時(shí)序公平的讀寫鎖詳解

    C++實(shí)現(xiàn)基于時(shí)序公平的讀寫鎖詳解

    讀寫鎖與普通的互斥鎖的區(qū)別在于有兩種上鎖方式:讀鎖和寫鎖,不用的用戶對(duì)同一個(gè)讀寫鎖獲取讀鎖是非互斥的,其他情況則是互斥的,本文小編將給大家詳細(xì)介紹C++實(shí)現(xiàn)基于時(shí)序公平的讀寫鎖,需要的朋友可以參考下
    2023-10-10
  • C數(shù)據(jù)結(jié)構(gòu)中串簡(jiǎn)單實(shí)例

    C數(shù)據(jù)結(jié)構(gòu)中串簡(jiǎn)單實(shí)例

    這篇文章主要介紹了C數(shù)據(jù)結(jié)構(gòu)中串簡(jiǎn)單實(shí)例的相關(guān)資料,需要的朋友可以參考下
    2017-06-06
  • C語(yǔ)言直接插入排序算法

    C語(yǔ)言直接插入排序算法

    大家好,本篇文章主要講的是C語(yǔ)言直接插入排序算法,感興趣的同學(xué)趕快來看一看吧,對(duì)你有幫助的話記得收藏一下,方便下次瀏覽
    2022-01-01
  • C++超詳細(xì)講解友元與內(nèi)部類

    C++超詳細(xì)講解友元與內(nèi)部類

    朋友們好,這篇播客我們繼續(xù)C++的初階學(xué)習(xí),現(xiàn)在對(duì)我們對(duì)C++的友元,內(nèi)部類知識(shí)點(diǎn)做出總結(jié),整理出來一篇博客供我們一起復(fù)習(xí)和學(xué)習(xí),如果文章中有理解不當(dāng)?shù)牡胤?還希望朋友們?cè)谠u(píng)論區(qū)指出,我們相互學(xué)習(xí),共同進(jìn)步
    2022-06-06
  • OpenCV圖像處理之實(shí)現(xiàn)圖像膨脹腐蝕操作

    OpenCV圖像處理之實(shí)現(xiàn)圖像膨脹腐蝕操作

    圖像形態(tài)學(xué)操作是指基于形狀的一系列圖像處理操作的合集,主要是基于集合論基礎(chǔ)上的形態(tài)學(xué)數(shù)學(xué)對(duì)圖像進(jìn)行處理。本文將為大家介紹一下如何利用OpenCV實(shí)現(xiàn)其中的腐蝕和膨脹操作,需要的可以參考一下
    2022-09-09
  • C++結(jié)構(gòu)體與類指針知識(shí)點(diǎn)總結(jié)

    C++結(jié)構(gòu)體與類指針知識(shí)點(diǎn)總結(jié)

    在本篇文章里小編給大家整理了關(guān)于C++結(jié)構(gòu)體與類指針知識(shí)點(diǎn)以及相關(guān)內(nèi)容,有興趣的朋友們參考學(xué)習(xí)下。
    2019-09-09
  • c++ vector 常用函數(shù)示例解析

    c++ vector 常用函數(shù)示例解析

    這篇文章主要介紹了c++ vector 常用函數(shù)示例解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-07-07
  • C語(yǔ)言分別實(shí)現(xiàn)棧和隊(duì)列詳解流程

    C語(yǔ)言分別實(shí)現(xiàn)棧和隊(duì)列詳解流程

    棧和隊(duì)列,嚴(yán)格意義上來說,也屬于線性表,因?yàn)樗鼈円捕加糜诖鎯?chǔ)邏輯關(guān)系為 "一對(duì)一" 的數(shù)據(jù),但由于它們比較特殊,因此將其單獨(dú)作為一章,做重點(diǎn)講解
    2022-04-04
  • 詳情介紹C++之命名空間

    詳情介紹C++之命名空間

    這篇文章主要詳情介紹了C++命名空間,命名空間的出現(xiàn)就是為了解決名稱沖突問題,對(duì)此感興趣的朋友可以參考下面文章
    2021-09-09

最新評(píng)論