最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

一文總結(jié)C++中的異常

 更新時(shí)間:2023年10月23日 11:54:17   作者:春人.  
異常是一種處理錯(cuò)誤的方式,當(dāng)一個(gè)函數(shù)發(fā)現(xiàn)自己無法處理的錯(cuò)誤時(shí)就可以拋出異常,讓函數(shù)的直接或間接調(diào)用者處理這個(gè)錯(cuò)誤,本文給大家總結(jié)了C++中的異常,需要的朋友可以參考下

一、C語言傳統(tǒng)的處理錯(cuò)誤的方式

傳統(tǒng)的錯(cuò)誤處理機(jī)制

  • 終止程序:如 assert,缺點(diǎn)是用戶難以接受,如發(fā)生內(nèi)存錯(cuò)誤,除 0 錯(cuò)誤時(shí)就會(huì)終止程序。

  • 返回錯(cuò)誤碼:缺陷是,需要程序員自己去查找錯(cuò)誤碼對(duì)應(yīng)的錯(cuò)誤,其次錯(cuò)誤碼需要層層返回,這就要求我們要去接收和檢查返回值。如系統(tǒng)的很多庫的接口函數(shù)都是通過把錯(cuò)誤碼放到 errno 中,表示錯(cuò)誤。

實(shí)際中 C 語言基本都是使用返回錯(cuò)誤碼的方式處理錯(cuò)誤,部分情況下使用終止程序處理非常嚴(yán)重的錯(cuò)誤。

二、C++異常

異常是一種處理錯(cuò)誤的方式,當(dāng)一個(gè)函數(shù)發(fā)現(xiàn)自己無法處理的錯(cuò)誤時(shí)就可以拋出異常,讓函數(shù)的直接或間接調(diào)用者處理這個(gè)錯(cuò)誤。

  • throw:當(dāng)問題出現(xiàn)時(shí),程序會(huì)拋出一個(gè)異常,這是通過使用 throw 關(guān)鍵字來完成的。

  • catch:在想要處理問題的地方,通過異常處理程序捕獲異常,catch 關(guān)鍵字用于捕獲異常,可以有多個(gè) catch 進(jìn)行捕獲。

  • try:try 塊中的代碼標(biāo)識(shí)將被激活的特定異常,它后面通常跟著一個(gè)或多個(gè) catch 塊。

如果有一個(gè)塊拋出一個(gè)異常,捕獲異常的方法會(huì)使用 try 和 catch 關(guān)鍵字,try 塊中放置可能拋出異常的代碼,try 塊中的代碼被稱為保護(hù)代碼。使用 try/catch 語句的語法如下所示:

try
{
  // 保護(hù)的標(biāo)識(shí)代碼
}catch( ExceptionName e1 )
{
  // catch 塊
}catch( ExceptionName e2 )
{
  // catch 塊
}catch( ExceptionName eN )
{
  // catch 塊
}

三、異常的使用

3.1 異常的拋出和捕獲

3.1.1 異常的拋出和匹配原則

  • 異常是通過拋出對(duì)象而引發(fā)的,該對(duì)象的類型決定了應(yīng)該激活哪個(gè) catch 的處理代碼。

  • 被選中的處理代碼是調(diào)用鏈中與該對(duì)象類型匹配且離拋出異常位置最近的那一個(gè)。

  • 拋出異常對(duì)象后,會(huì)生成一個(gè)異常對(duì)象的拷貝,因?yàn)閽伋龅漠惓?duì)象可能是一個(gè)臨時(shí)對(duì)象,所以會(huì)生成一個(gè)拷貝對(duì)象,這個(gè)拷貝的臨時(shí)對(duì)象會(huì)在被 catch 以后銷毀(這里的處理類似于函數(shù)的傳值返回)。

  • catch(…) 可以捕獲任意類型的異常,問題是不知道異常錯(cuò)誤是什么。

  • 實(shí)際中拋出和捕獲的匹配原則有個(gè)例外,并不都是類型完全匹配,可以拋出派生類對(duì)象使用基類捕獲,這個(gè)在實(shí)際中非常實(shí)用,后面會(huì)進(jìn)行詳細(xì)講解。

小Tips:異??梢話伋鋈魏晤愋偷膶?duì)象,即 throw 后面可以跟任意類型。異常拋出后如果沒有被捕獲,那么程序?qū)?huì)報(bào)錯(cuò)。沒有捕獲可能是因?yàn)轭愋筒黄ヅ鋵?dǎo)致沒有捕獲,也可能是壓根就沒有 catch 語句去捕獲。catch(…) 只能放在所有 catch 的最后。

3.1.2 在函數(shù)調(diào)用鏈中異常棧展開匹配原則

  • 首先檢查 throw 本身是否在 try 塊內(nèi)部,如果是,再查找匹配的 catch 語句。如果有匹配的,則調(diào)到 catch 的地方進(jìn)行處理。

  • 沒有匹配的 catch 則退出當(dāng)前函數(shù)棧幀,繼續(xù)在調(diào)用函數(shù)的棧幀中進(jìn)行查找匹配的 catch。

  • 如果到達(dá) main 函數(shù)的棧幀,依舊沒有匹配的,則終止程序。上述這個(gè)沿著調(diào)用鏈查找匹配的 catch 子句的過程稱為棧展開。所以實(shí)際中我們最后都要加一個(gè)catch(…)捕獲任意類型的異常,否則當(dāng)有異常沒捕獲,程序就會(huì)直接終止。

  • 找到匹配的 catch 字句并處理以后,會(huì)繼續(xù)沿著 catch 字句后面繼續(xù)執(zhí)行。

如上圖所示:有三個(gè)函數(shù) func1()、func2()、func3()。在 func2() 中調(diào)用 func1(),func3() 中調(diào)用 func2(),在 main() 中調(diào)用 func3(),并在 func1() 中拋出一個(gè)異常,在 main() 中用 catch 語句捕獲。

#include <iostream>
using namespace std;

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);
}
void Func()
{
    int len, time;
    cin >> len >> time;
    cout << Division(len, time) << endl;
}
int main()
{
    try 
    {
        Func();
    }
    catch (const char* errmsg)
    {
        cout << errmsg << endl;
    }
    catch (...) 
    {
        cout << "unkown exception" << endl;
    }
    return 0;
}

棧展開的過程如下:首先檢查 throw 本身是否在 try 塊內(nèi)部,如果是,再查找匹配的 catch 語句。如果有匹配的,則處理。如果沒有則退出當(dāng)前函數(shù)棧,繼續(xù)在調(diào)用函數(shù)的棧中進(jìn)行查找,不斷重復(fù)上述過程,若到達(dá) main 函數(shù)額的棧,依舊沒有找到匹配的,則終止程序。

3.2 異常的重新拋出

有可能單個(gè)的 catch 不能完全處理一個(gè)異常,在進(jìn)行一些校正后,希望在交給更外層的調(diào)用鏈函數(shù)來處理,catch 則可以通過重新拋出將異常傳遞給更上層的函數(shù)進(jìn)行處理。

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沒有得到釋放。
	// 所以這里捕獲異常后并不處理異常,異常還是交給外面處理,這里捕獲了再
	// 重新拋出去。
	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;
}

小Tips:如上面的帶所示,在 Func() 層捕獲了異常之后,并沒有對(duì)異常進(jìn)行“處理”,而是將 array 進(jìn)行了釋放,然后再將異常重新拋出去。Func() 層如果不對(duì)異常進(jìn)行捕獲,那么在發(fā)生除零錯(cuò)誤后,array 指向的空間就不會(huì)被釋放,最終造成內(nèi)存泄漏。

3.3 異常安全

  • 構(gòu)造函數(shù)完成對(duì)象的構(gòu)造和初始化,最好不要在構(gòu)造函數(shù)中拋出異常,否則可能導(dǎo)致對(duì)象不完整或沒有完全初始化。

  • 析構(gòu)函數(shù)主要完成資源的清理,最好不要在析構(gòu)函數(shù)中拋出異常,否則可能導(dǎo)致資源泄露(內(nèi)存泄漏、句柄未關(guān)閉等)。

  • C++中異常經(jīng)常會(huì)導(dǎo)致資源泄露的問題,比如在 new 和 delete 中拋出異常,導(dǎo)致內(nèi)存泄漏,在 lock 和 unlock 之間拋出了異常導(dǎo)致死鎖,C++經(jīng)常使用 RAII 來解決以上問題,關(guān)于 RAII 將在智能指針中為大家講解。

3.4 異常規(guī)范

  • 異常規(guī)范說明的目的是為了讓函數(shù)使用者知道該函數(shù)可能拋出的異常有哪些??梢栽诤瘮?shù)的后面接 throw(類型),列出這個(gè)函數(shù)可能拋出的所有異常類型。

  • 函數(shù)的后面接 throw(),表示函數(shù)不拋異常。

  • 若無異常接口聲明,則此函數(shù)可以拋出任何類型的異常。

// 這里表示這個(gè)函數(shù)會(huì)拋出A/B/C/D中的某種類型的異常
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;

小Tips:C++11之后,一個(gè)函數(shù)如果不拋異常,就在其函數(shù)后面加上一個(gè) noexcept,如果不拋異常可以不做任何處理。

四、自定義異常體系

實(shí)際使用中,很多公司都會(huì)自定義自己的異常體系進(jìn)行規(guī)范的異常管理,因?yàn)橐粋€(gè)項(xiàng)目中如果大家隨意拋異常,那么外層的調(diào)用者基本就沒辦法玩了,所以實(shí)際中都會(huì)定義一套繼承的規(guī)范體系,這樣大家拋出的都是繼承自父類的派生類對(duì)象,捕獲一個(gè)基類就可以了。

// 服務(wù)器開發(fā)中通常使用的異常繼承體系
class Exception
{
public:
	Exception(const string& errmsg, int id)
		:_errmsg(errmsg)
		, _id(id)
	{}

	virtual string what() const
	{
		return _errmsg;
	}
protected:
	string _errmsg;
	int _id;
};

class SqlException : public Exception
{
public:
	SqlException(const string& errmsg, int id, const string& sql)
		:Exception(errmsg, id)
		, _sql(sql)
	{}

	virtual string what() const
	{
		string str = "SqlException:";
		str += _errmsg;
		str += "->";
		str += _sql;

		return str;
	}

private:
	const string _sql;
};

class CacheException : public Exception
{
public:
	CacheException(const string& errmsg, int id)
		:Exception(errmsg, id)
	{}

	virtual string what() const
	{
		string str = "CacheException:";
		str += _errmsg;
		return str;
	}
};

class HttpServerException : public Exception
{
public:
	HttpServerException(const string& errmsg, int id, const string& type)
		:Exception(errmsg, id)
		, _type(type)
	{}

	virtual string what() const
	{
		string str = "HttpServerException:";
		str += _type;
		str += ":";
		str += _errmsg;

		return str;
	}

private:
	const string _type;
};


void SQLMgr()
{
	srand(time(0));
	if (rand() % 7 == 0)
	{
		throw SqlException("權(quán)限不足", 100, "select * from name = '張三'");
	}

	//throw "xxxxxx";

	cout << "執(zhí)行成功" << endl;
}

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();
}

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();
}

int main()
{
	while (1)
	{
		Sleep(500);

		try {
			HttpServer();
		}
		catch (const Exception& e) // 這里捕獲父類對(duì)象就可以
		{
			// 多態(tài)
			cout << e.what() << endl;
		}
		catch (...)
		{
			cout << "Unkown Exception" << endl;
		}
	}

	return 0;
}

五、C++標(biāo)準(zhǔn)庫的異常體系

C++提供了一些列標(biāo)準(zhǔn)的異常,定義在 exception 中,我們可以在程序中使用這些標(biāo)準(zhǔn)的異常,它們是以父子類層次結(jié)構(gòu)組織起來的,如下所示:

小Tips:實(shí)際中我們可以通過去繼承 exception 類實(shí)現(xiàn)自己的異常類。但是實(shí)際中很多公司像上面一樣,自己定義一套異常繼承體系。因?yàn)?C++ 標(biāo)準(zhǔn)庫設(shè)計(jì)的不夠好用。

int main()
{
	try 
	{
		vector<int> v(10, 5);
		// 這里如果系統(tǒng)內(nèi)存不夠也會(huì)拋異常
		v.reserve(1000000000);
		// 這里越界會(huì)拋異常
		v.at(10) = 100;
	}
	catch (const exception& e) // 這里捕獲父類對(duì)象就可以
	{
		cout << e.what() << endl;
	}
	catch (...)
	{
		cout << "Unkown Exception" << endl;
	}
	return 0;
}

六、異常的優(yōu)缺點(diǎn)

6.1 優(yōu)點(diǎn)

  • 異常對(duì)象定義好了,相比錯(cuò)誤碼的方式可以清晰準(zhǔn)確的展示出錯(cuò)誤的各種信息,甚至可以包含堆棧調(diào)用的信息,這樣可以幫助更好的定位程序的 bug。

  • 很多的第三方庫都包含異常,比如 boost、gtest、gmock 等等常用的庫,那么我們使用他們也需要使用異常。

  • 部分函數(shù)使用異常更好處理,比如構(gòu)造函數(shù)沒有返回值,不方便使用錯(cuò)誤碼的方式處理。比如 T& operator[ ] 這樣的函數(shù),如果 pos 越界了只能使用異?;蛘呓K止程序處理,沒辦法通過返回值表示錯(cuò)誤。

  • 返回錯(cuò)誤碼的傳統(tǒng)方式有個(gè)很大的問題就是,在函數(shù)調(diào)用鏈中,深層的函數(shù)返回了錯(cuò)誤,那么我們得層層返回錯(cuò)誤,最外層才能拿到錯(cuò)誤,具體看下面的詳細(xì)解釋。

 // 1.下面這段偽代碼我們可以看到ConnnectSql中出錯(cuò)了,先返回給ServerStart,
//ServerStart再返回給main函數(shù),main函數(shù)再針對(duì)問題處理具體的錯(cuò)誤。
// 2.如果是異常體系,不管是ConnnectSql還是ServerStart及調(diào)用函數(shù)出錯(cuò),都不用檢查,因
//為拋出的異常異常會(huì)直接跳到main函數(shù)中catch捕獲的地方,main函數(shù)直接處理錯(cuò)誤。
int ConnnectSql()
{
    // 用戶名密碼錯(cuò)誤
    if (...)
        return 1;

    // 權(quán)限不足
    if (...)
        return 2;
}

int ServerStart() {
    if (int ret = ConnnectSql() < 0)
        return ret;
    int fd = socket()
        if(fd < 0)
        return errno;
}

int main()
{
    if (ServerStart() < 0)
        ...

        return 0;
}

6.2 缺點(diǎn)

  • 異常會(huì)導(dǎo)致程序的執(zhí)行流亂跳,并且非常的混亂,并且是運(yùn)行時(shí)出錯(cuò)拋異常就會(huì)亂跳。這會(huì)導(dǎo)致我們跟蹤調(diào)試時(shí)以及分析程序時(shí),比較困難。

  • 異常會(huì)有一些性能的開銷(拋出異常后,會(huì)生成一個(gè)異常對(duì)象的拷貝)。當(dāng)然在現(xiàn)代硬件速度很快的情況下,這個(gè)影響基本忽略不計(jì)。

  • C++沒有垃圾回收機(jī)制,資源需要自己管理,有了異常非常容易導(dǎo)致內(nèi)存泄漏、死鎖等異常安全問題。這個(gè)需要使用 RAII 來處理資源的管理問題。學(xué)習(xí)成本較高。

  • C++標(biāo)準(zhǔn)庫的異常體系定義的不好,導(dǎo)致大家各自定義各自的異常體系,非常的混亂。

  • 異常盡量規(guī)范使用,否則后果不堪設(shè)想,隨意拋異常,外層捕獲的用戶苦不堪言。所以異常規(guī)范有兩點(diǎn):一是:拋出的異常類型都繼承自一個(gè)基類;二是:函數(shù)是否拋異常、拋什么異常,都使用 func() throw(); 的方式規(guī)范化。

總結(jié):異常總體而言,利大于弊,所以工程中我們還是鼓勵(lì)使用異常的。另外 OO 的語言基本都是用異常處理錯(cuò)誤,這也可以看出這是大勢(shì)所趨。

七、結(jié)語

以上就是一文總結(jié)C++中的異常的詳細(xì)內(nèi)容,更多關(guān)于C++異常的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • c語言實(shí)現(xiàn)簡(jiǎn)易版三子棋(附完整代碼)

    c語言實(shí)現(xiàn)簡(jiǎn)易版三子棋(附完整代碼)

    大家好,本篇文章主要講的是c語言實(shí)現(xiàn)簡(jiǎn)易版三子棋(附完整代碼),感興趣的同學(xué)趕快來看一看吧,對(duì)你有幫助的話記得收藏一下
    2022-01-01
  • 利用Matlab繪制有趣圖像的示例代碼

    利用Matlab繪制有趣圖像的示例代碼

    這篇文章主要為大家總結(jié)了一些利用Matlab繪制的有趣好看的圖像的示例代碼。文中的示例代碼簡(jiǎn)潔易懂,感興趣的小伙伴可以動(dòng)手試一試
    2022-03-03
  • EasyC++單獨(dú)編譯

    EasyC++單獨(dú)編譯

    這篇文章主要介紹了EasyC++單獨(dú)編譯,在上一篇當(dāng)中,我們編寫好了頭文件coordin.h,現(xiàn)在我們要完成它的實(shí)現(xiàn)。需要的小伙伴可以先學(xué)習(xí)上一篇內(nèi)容然后一起與小編一起進(jìn)入本篇內(nèi)容一起學(xué)習(xí)吧
    2021-12-12
  • C語言強(qiáng)制類型轉(zhuǎn)換規(guī)則實(shí)例詳解

    C語言強(qiáng)制類型轉(zhuǎn)換規(guī)則實(shí)例詳解

    強(qiáng)制類型轉(zhuǎn)換是把變量從一種類型轉(zhuǎn)換為另一種數(shù)據(jù)類型,下面這篇文章主要給大家介紹了關(guān)于C語言強(qiáng)制類型轉(zhuǎn)換的相關(guān)資料,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2022-06-06
  • C程序中可怕的野指針圖文詳解

    C程序中可怕的野指針圖文詳解

    這篇文章主要給大家介紹了關(guān)于C程序中可怕的野指針的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用C程序具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-07-07
  • 使用C/C++讀取matlab中.mat格式數(shù)據(jù)的操作

    使用C/C++讀取matlab中.mat格式數(shù)據(jù)的操作

    這篇文章給大家介紹了使用C/C++讀取matlab中.mat格式數(shù)據(jù)的操作,文中通過圖文結(jié)合的方式介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作有一定的幫助,需要的朋友可以參考下
    2023-12-12
  • C語言中快速排序和插入排序優(yōu)化的實(shí)現(xiàn)

    C語言中快速排序和插入排序優(yōu)化的實(shí)現(xiàn)

    這篇文章主要介紹了C語言中快速排序和插入排序優(yōu)化的實(shí)現(xiàn),包括雙向劃分快速排序方法的介紹,需要的朋友可以參考下
    2015-11-11
  • 使用OpenGL創(chuàng)建窗口的示例詳解

    使用OpenGL創(chuàng)建窗口的示例詳解

    OpenGL,也就是Open?Graphics?Library。其主要就是用于我們?nèi)ヤ秩?D、3D矢量圖形的一種跨語言、跨平臺(tái)的應(yīng)用程序編程接口,這篇文章主要介紹了使用OpenGL創(chuàng)建窗口,需要的朋友可以參考下
    2022-04-04
  • 使用C語言計(jì)算長(zhǎng)方體的表面積和體積

    使用C語言計(jì)算長(zhǎng)方體的表面積和體積

    這篇文章主要給大家介紹了關(guān)于如何使用C語言計(jì)算長(zhǎng)方體的表面積和體積的相關(guān)資料,在C語言中,我們可以使用乘法運(yùn)算符(*)來進(jìn)行乘法運(yùn)算,并將結(jié)果保存在一個(gè)變量中,需要的朋友可以參考下
    2023-10-10
  • C語言中循環(huán)語句練習(xí)實(shí)例

    C語言中循環(huán)語句練習(xí)實(shí)例

    大家好,本篇文章主要講的是C語言中循環(huán)語句練習(xí)實(shí)例,感興趣的同學(xué)趕快來看一看吧,對(duì)你有幫助的話記得收藏一下
    2022-01-01

最新評(píng)論

新建县| 渑池县| 岱山县| 平邑县| 什邡市| 邹平县| 香河县| 始兴县| 绥滨县| 陆良县| 平湖市| 台山市| 定安县| 三明市| 莫力| 赤城县| 河源市| 阜城县| 西平县| 吕梁市| 松溪县| 韶山市| 新兴县| 霞浦县| 高邑县| 都兰县| 天津市| 宁海县| 泰兴市| 玉山县| 松原市| 互助| 唐山市| 潜江市| 凌海市| 斗六市| 屏东市| 垦利县| 巴塘县| 苍南县| 修文县|