欧美free性护士vide0shd,老熟女,一区二区三区,久久久久夜夜夜精品国产,久久久久久综合网天天,欧美成人护士h版

首頁綜合 正文
目錄

柚子快報(bào)激活碼778899分享:【C++】異常

柚子快報(bào)激活碼778899分享:【C++】異常

http://yzkb.51969.com/

異常

1.C語言傳統(tǒng)的處理錯(cuò)誤的方式2.C++異常概念3.異常的使用4.自定義異常體系5.C++標(biāo)準(zhǔn)庫的異常體系6.異常的優(yōu)缺點(diǎn)

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

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

終止程序,如assert,缺陷:用戶難以接受。如發(fā)生內(nèi)存錯(cuò)誤,除0錯(cuò)誤時(shí)就會終止程序。返回錯(cuò)誤碼,缺陷:需要程序員自己去查找對應(yīng)的錯(cuò)誤。如系統(tǒng)的很多庫的接口函數(shù)都是通過把錯(cuò)誤碼放到errno中,表示錯(cuò)誤

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

C++更喜歡用異常來處理錯(cuò)誤。

2.C++異常概念

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

throw: 當(dāng)問題出現(xiàn)時(shí),程序會拋出一個(gè)異常。這是通過使用 throw 關(guān)鍵字來完成的。catch: 在您想要處理問題的地方,通過異常處理程序捕獲異常.catch 關(guān)鍵字用于捕獲異常,可以有多個(gè)catch進(jìn)行捕獲。try: try 塊中的代碼標(biāo)識將被激活的特定異常,它后面通常跟著一個(gè)或多個(gè) catch 塊。

thorw拋異常拋的的是一個(gè)任意類型對象。catch捕捉異常注意一定要類型匹配,否則就出錯(cuò)。

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

try

{

// 保護(hù)的標(biāo)識代碼

}catch( ExceptionName e1 )

{

// catch 塊

}catch( ExceptionName e2 )

{

// catch 塊

}catch( ExceptionName eN )

{

// catch 塊

}

我們先看看異常怎么用的,再說其他細(xì)節(jié)!

下面這段代碼出現(xiàn)除0錯(cuò)誤程序就會終止。但我不想讓程序終止,因此當(dāng)被除數(shù)為0就拋一個(gè)異常,很簡單就是用throw后面加一個(gè)對象,可以是任意類型對象如int、string等等。后面catch對異常進(jìn)行捕獲。

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;

}

return 0;

}

下面以調(diào)試的方式看看沒有異常發(fā)生程序是怎么走的。 可以看到當(dāng)沒有拋異常發(fā)生時(shí)候,就會跳過catch這一句。

再看發(fā)生異常怎么走的。

可以看到當(dāng)拋除異常的時(shí)候后面代碼就不會在執(zhí)行了,本來division執(zhí)行完會回到Func的,而現(xiàn)在會直接跳轉(zhuǎn)到catch然后往下在順序走。注意沒有catch就會報(bào)錯(cuò)。

異常相比于C語言錯(cuò)誤的優(yōu)勢在于。C語言返回的是錯(cuò)誤碼1、2、3等等,還需要去查錯(cuò)誤碼表。而這里直接告訴錯(cuò)誤是什么。

3.異常的使用

異常的拋出和匹配原則

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

實(shí)際上可能會有多個(gè)catch進(jìn)行捕捉,但是即使有多個(gè)catch,只會進(jìn)類型匹配的catch。并且只會進(jìn)一個(gè)catch,其他catch的都會跳過。 注意捕獲只會捕捉try里面的對象。

如果catch類型不匹配會發(fā)生什么事情?

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 (char* errmsg)//拋出的是一個(gè)const char*,而catch這里是char*

{

cout << errmsg << endl;

}

catch (int errid)

{

cout << errid << endl;

}

return 0;

}

注意,當(dāng)拋除一個(gè)異常沒有任何類型可以匹配,就會直接終止程序。

并且也不允許兩個(gè)相同類型catch進(jìn)行捕獲!

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

兩個(gè)條件一個(gè)是類型匹配我們已經(jīng)知道了,第二個(gè)是離拋出異常位置最近我們看一看。

看下面代碼,現(xiàn)在Func中也有catch,main也有catch,會走哪一個(gè)?如果catch之后后面代碼如何執(zhí)行?

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

{

try{

int len, time;

cin >> len >> time;

cout << Division(len, time) << endl;

}

catch (const char* errmsg)

{

cout << errmsg << endl;

}

cout<<"1111111"<

}

int main()

{

try {

Func();

}

catch (const char* errmsg)

{

cout << errmsg << endl;

}

catch (int errid)

{

cout << errid << endl;

}

return 0;

}

這里涉及調(diào)用鏈的概念,棧幀的調(diào)用就是它所說的調(diào)用鏈。

調(diào)用鏈中與該對象類型匹配且離拋出異常位置最近的那一個(gè)。

異常只要被捕獲,后面都是正常執(zhí)行的了,注意只捕獲一次!即使其他調(diào)用鏈還有捕獲也不會就進(jìn)去。

在看下面的問題。 如果中間不捕獲會發(fā)生什么問題?

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* p = new int[10];

int len, time;

cin >> len >> time;

cout << Division(len, time) << endl;

cout << "delete" << p << endl;

delete[] p;

}

int main()

{

try {

Func();

}

catch (const char* errmsg)

{

cout << errmsg << endl;

}

catch (int errid)

{

cout << errid << endl;

}

return 0;

}

這是不是鐵鐵的內(nèi)存泄露啊,因?yàn)橹苯犹D(zhuǎn)到catch了。 現(xiàn)在異常來了對new,delete,malloc,free造成了極大的困擾。以前所說的內(nèi)存泄露是忘記釋放了,現(xiàn)在即使自己寫了可能執(zhí)行不了,因?yàn)橹虚g那部分可能會拋異常,這就很難受!

這里有兩種解決方式。 最簡單的就是把異常捕獲掉。

}

void Func()

{

int* p = new int[10];

try{

int len, time;

cin >> len >> time;

cout << Division(len, time) << endl;

}

catch (const char* errmsg)

{

cout << errmsg << endl;

}

cout << "delete" << p << endl;

delete[] p;

}

萬一期望在最外面地方mian中進(jìn)行統(tǒng)一捕獲,記錄日志,進(jìn)行統(tǒng)一處理。怎么做?

日志是一個(gè)很好的東西。第一調(diào)試只能在debug版本,release版本沒法調(diào)試。第二公司代碼很長小一點(diǎn)就幾萬行幾十萬行,大一點(diǎn)幾百萬幾千萬都有,調(diào)試就只是很微小的手段。所以使用日志把錯(cuò)誤記錄下來,然后通過日志逐個(gè)分析追溯問題。

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* p = new int[10];

try{

int len, time;

cin >> len >> time;

cout << Division(len, time) << endl;

}

catch (const char* errmsg)

{

//cout << errmsg << endl;

cout << "delete" << p << endl;

delete[] p;

throw errmsg;//重新拋除

}

}

int main()

{

try {

Func();

}

catch (const char* errmsg)

{

//記錄日志,進(jìn)行統(tǒng)一處理

cout << errmsg << endl;

}

catch (int errid)

{

cout << errid << endl;

}

return 0;

}

這種方法很不好,后面有一種專門解決這里的問題方式智能指針,后面學(xué)到再說。

再看其他的問題。 可能不止一個(gè)函數(shù)會拋異常,如func也會拋異常,如果這里寫很多catch就很難受。

void Func()

{

int* p = new int[10];

try{

int len, time;

cin >> len >> time;

cout << Division(len, time) << endl; //throw const char*對象

//func() throw double對象

}

catch (const char* errmsg)

{

//cout << errmsg << endl;

cout << "delete" << p << endl;

delete[] p;

throw errmsg;//重新拋除

}

}

因此這里可以這樣寫

void Func()

{

int* p = new int[10];

try{

int len, time;

cin >> len >> time;

cout << Division(len, time) << endl; //throw const char*

//func() throw double

}

catch (...)//...表示可以捕獲任意類型異常,缺點(diǎn)就是不知道捕獲什么類型對象

{

cout << "delete" << p << endl;

delete[] p;

//因此可以這樣寫

throw;//重新拋除,捕獲到什么就拋除什么

}

}

3.拋出異常對象后,會生成一個(gè)異常對象的拷貝,因?yàn)閽伋龅漠惓ο罂赡苁且粋€(gè)臨時(shí)對象,所以會生成一個(gè)拷貝對象,這個(gè)拷貝的臨時(shí)對象會在被catch以后銷毀。(這里的處理類似于函數(shù)的傳值返回)

double Division(int a, int b)

{

// 當(dāng)b == 0時(shí)拋出異常

if (b == 0)

{

string str("Division by zero condition!");

throw str;//拋異常,拋出一個(gè)臨時(shí)對象,出了作用域就銷毀了

}

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 string& s)//這里盡量用&,不然就是一個(gè)拷貝了

{

//這里捕獲的s是臨時(shí)對象str嗎?

//并不是,捕獲的是str的拷貝。所以說的是類似于函數(shù)的傳值返回

//以前沒有移動構(gòu)造效率有點(diǎn)低,現(xiàn)在編譯器在識別str會把它識別成一個(gè)將亡值就會調(diào)用移動構(gòu)造效率還是可以的

//不管是深拷貝還是移動構(gòu)造,這里catch拿到的都是一個(gè)拷貝并不是本身。

cout << s << endl;

}

catch (int errid)

{

cout << errid << endl;

}

return 0;

}

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

int main()

{

try {

Func();

}

catch (int errid)

{

cout << errid << endl;

}

return 0;

}

別人亂拋異常,但是類型不匹配,程序就會終止。 但是即使類型不匹配,也不希望程序被隨意終止。 就如使用微信發(fā)信息但是沒發(fā)出去,原因有很多??赡軇e人給你刪了,可能因?yàn)榫W(wǎng)絡(luò)不好發(fā)不出去。也不可能因?yàn)闆]捕獲到這里異常就把微信給退了。

int main()

{

try {

Func();

}

catch (int errid)

{

cout << errid << endl;

}

catch (...)//類型不匹配走這里,不至于讓程序退出。別人的程序可能在一個(gè)循環(huán)里面并不像這里捕獲了然后退出了。

{

cout << "未知異常" << endl;

}

return 0;

}

5.實(shí)際中拋出和捕獲的匹配原則有個(gè)例外,并不都是類型完全匹配,可以拋出的派生類對象,使用基類捕獲,這個(gè)在實(shí)際中非常實(shí)用。下面我們在看

打個(gè)比方,有一個(gè)項(xiàng)目組里有30個(gè)人,這里會有這樣的問題,每個(gè)人都拋一個(gè)異常,那該怎么辦?那不是把最外面捕獲異常的人搞死了。 這個(gè)時(shí)候就有這個(gè)終極解決方式,就是第5條。可以拋派生類用基類捕獲。而且還能玩多態(tài)。這是多數(shù)人的選擇。

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

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

自己這個(gè)地方也可以try catch但是一般不這么做。

double Division(int a, int b)

{

try {

// 當(dāng)b == 0時(shí)拋出異常

if (b == 0)

{

string str("Division by zero condition!");

throw str;//拋string對象

}

else

{

return ((double)a / (double)b);

}

}

catch (const string& s)

{

cout << s << endl;

}

}

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

異常的重新拋出

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

異常安全

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

構(gòu)造函數(shù)有10個(gè)對象,只初始了5個(gè)拋了一個(gè)異常就跳轉(zhuǎn)走了。

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

可能析構(gòu)函數(shù)跑了一半,然后在中間拋了一個(gè)異常導(dǎo)致資源泄漏。

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

異常規(guī)范

異常規(guī)格說明的目的是為了讓函數(shù)使用者知道該函數(shù)可能拋出的異常有哪些。 可以在函數(shù)的后面接throw(類型),列出這個(gè)函數(shù)可能拋擲的所有異常類型。函數(shù)的后面接throw(),表示函數(shù)不拋異常。若無異常接口聲明,則此函數(shù)可以拋擲任何類型的異常。

C++98這套拋異常復(fù)雜,也沒有強(qiáng)制形同虛設(shè)。

// 這里表示這個(gè)函數(shù)會拋出A/B/C/D中的某種類型的異常

void fun() throw(A,B,C,D);

// 這里表示這個(gè)函數(shù)只會拋出bad_alloc的異常

void* operator new (std::size_t size) throw (std::bad_alloc);

// 這里表示這個(gè)函數(shù)不會拋出異常

void* operator delete (std::size_t size, void* ptr) throw();

C++11加了一個(gè)異常規(guī)范,也不需要你到底會不會拋異常而簡化一下,不拋異常就把noexcept加上。那就知道你不會拋異常。但是編譯器還是會檢測一下給一個(gè)警告。沒加noexcept就可能拋異常那就需要小心了。

// C++11 中新增的noexcept,表示不會拋異常

thread() noexcept;

thread (thread&& x) noexcept;

4.自定義異常體系

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

};

這個(gè)基類至少要包含兩個(gè)信息,一個(gè)id一個(gè)字符串,這個(gè)字符串描述了錯(cuò)誤信息,這個(gè)id要給這些錯(cuò)誤編個(gè)碼主要用于對于某些錯(cuò)誤進(jìn)行特殊處理,就比如一個(gè)微信聊天,如果你被別人刪了發(fā)信息就直接給一個(gè)感嘆號,還有一種可能就是網(wǎng)絡(luò)錯(cuò)誤發(fā)不出去,出現(xiàn)網(wǎng)絡(luò)錯(cuò)誤可能不是直接報(bào)錯(cuò),可能會多嘗試幾次,如果超過10次都沒發(fā)出去就報(bào)錯(cuò)。

下面我們看一段代碼。搞出一些派生類對基類的繼承。

以數(shù)據(jù)庫層的異常為例解釋說明,繼承基類然后在搞一個(gè)_sql。根據(jù)以前的知識,派生類初始化基類調(diào)用基類的構(gòu)造初始化,然后在調(diào)用一個(gè)自己的。同時(shí)父還搞了一個(gè)虛函數(shù),子類重寫了虛函數(shù)。

class Exception//基類

{

public:

Exception(const string& errmsg, int id)

:_errmsg(errmsg)

, _id(id)

{}

virtual string what() const

{

return _errmsg;

}

protected:

string _errmsg;

int _id;

};

//數(shù)據(jù)庫

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:";//這里搞出一個(gè)標(biāo)識,表面著那個(gè)模塊的異常

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;

};

下面模擬一些拋異常,也是繼承和多態(tài)的應(yīng)用。

void SQLMgr()

{

srand(time(0));

if (rand() % 7 == 0)

{

throw SqlException("權(quán)限不足", 100, "select * from name = '張三'");

}

cout << "調(diào)用成功" << 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()

{

// 模擬發(fā)生錯(cuò)誤

srand(time(0));

if (rand() % 3 == 0)

{

throw HttpServerException("請求資源不存在", 100, "get");

}

else if (rand() % 4 == 0)

{

throw HttpServerException("權(quán)限不足", 101, "post");

}

CacheMgr();

}

int main()

{

while (1)

{

Sleep(1000);

try {

HttpServer();

}

catch (const Exception& e) // 這里捕獲父類對象就可以

{

//滿足多態(tài)調(diào)用,看父類的引用引用的是那個(gè)子類就調(diào)用那個(gè)子類的what()。就可以看到哪里出現(xiàn)問題,

cout << e.what() << endl;

}

catch (...)//最后一道防線,以防別人亂拋異常

{

cout << "Unkown Exception" << endl;

}

}

return 0;

}

這里就很清楚的知道到底是誰發(fā)生了錯(cuò)誤并且是什么錯(cuò)誤,每個(gè)組需要自己的異常也沒關(guān)系你自己去繼承,然后你想加什么成員都隨你。

一般只需要捕獲兩個(gè)東西就可以,一個(gè)是捕捉基類,一個(gè)是捕獲未知異常,萬一有人沒有按規(guī)范去走,不至于讓服務(wù)奔潰,只是這一次請求失敗了。 如果沒有捕捉未知異常程序就可能會奔潰。

同時(shí)捕獲父類和子類可以嗎? 是可以的。

int main()

{

while (1)

{

Sleep(1000);

try {

HttpServer();

}

catch (const Exception& e) // 這里捕獲父類對象就可以

{

// 多態(tài)

cout << e.what() << endl;

}

catch (const CacheException& e)

{

cout << "11111111" << e.what() << endl;

}

catch (...)

{

cout << "Unkown Exception" << endl;

}

}

return 0;

}

在猜一猜會不會走下面的CacheException呢? 可以看到并不會走CacheException。

如果把CacheException放在上面呢? 可以看到誰在上面調(diào)用誰,但是一般不這樣做,除非想對CacheException異常進(jìn)行特殊處理。

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

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

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

int main()

{

try {

vector v(10, 5);

// 這里如果系統(tǒng)內(nèi)存不夠也會拋異常

v.reserve(1000000000);

// 這里越界會拋異常

v.at(10) = 100;

}

catch (const exception& e) // 這里捕獲父類對象就可以

{

cout << e.what() << endl;

}

catch (...)

{

cout << "Unkown Exception" << endl;

}

return 0;

}

像上面自定義異常體系我們也可以加一個(gè)這樣的catch

int main()

{

while (1)

{

Sleep(1000);

try {

HttpServer();

}

catch (const Exception& e) // 這里捕獲父類對象就可以

{

// 多態(tài)

cout << e.what() << endl;

}

catch (const exception& e)//C++標(biāo)準(zhǔn)庫的異常體系

{

cout << e.what() << endl;

}

catch (...)

{

cout << "Unkown Exception" << endl;

}

}

return 0;

}

6.異常的優(yōu)缺點(diǎn)

C++異常的優(yōu)點(diǎn):

異常對象定義好了,相比錯(cuò)誤碼的方式可以清晰準(zhǔn)確的展示出錯(cuò)誤的各種信息,甚至可以包含堆棧調(diào)用的信息,這樣可以幫助更好的定位程序的bug。返回錯(cuò)誤碼的傳統(tǒng)方式有個(gè)很大的問題就是,在函數(shù)調(diào)用鏈中,深層的函數(shù)返回了錯(cuò)誤,那么我們得層層返回錯(cuò)誤,最外層才能拿到錯(cuò)誤,具體看下面的詳細(xì)解釋。

// 1.下面這段偽代碼我們可以看到ConnnectSql中出錯(cuò)了,先返回給ServerStart,

//ServerStart再返回給main函數(shù),main函數(shù)再針對問題處理具體的錯(cuò)誤。

// 2.如果是異常體系,不管是ConnnectSql還是ServerStart及調(diào)用函數(shù)出錯(cuò),都不用檢查,

//因?yàn)閽伋龅漠惓.惓苯犹絤ain函數(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;

}

很多的第三方庫都包含異常,比如boost、gtest、gmock等等常用的庫,那么我們使用它們也需要使用異常。部分函數(shù)使用異常更好處理,比如構(gòu)造函數(shù)沒有返回值,不方便使用錯(cuò)誤碼方式處理。比如T& operator這樣的函數(shù),如果pos越界了只能使用異常或者終止程序處理,沒辦法通過返回值表示錯(cuò)誤。

T& operator[](size_t pos)

{

//這里用不上返回錯(cuò)誤碼的方式,要不就是用斷言要不就是拋異常

//如果返回錯(cuò)誤碼返回的是T(),萬一某個(gè)位置的值就是缺省對象呢?

if (pos >= _size)

throw out_of_range("越界訪問");

return _start[pos];

}

C++異常的缺點(diǎn):

異常會導(dǎo)致程序的執(zhí)行流亂跳,并且非常的混亂,并且是運(yùn)行時(shí)出錯(cuò)拋異常就會亂跳。這會導(dǎo)致我們跟蹤調(diào)試時(shí)以及分析程序時(shí),比較困難。異常會有一些性能的開銷。當(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() noexcept;的方式規(guī)范化。

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

柚子快報(bào)激活碼778899分享:【C++】異常

http://yzkb.51969.com/

本文內(nèi)容根據(jù)網(wǎng)絡(luò)資料整理,出于傳遞更多信息之目的,不代表金鑰匙跨境贊同其觀點(diǎn)和立場。

轉(zhuǎn)載請注明,如有侵權(quán),聯(lián)系刪除。

本文鏈接:http://gantiao.com.cn/post/2027354963.html

發(fā)布評論

您暫未設(shè)置收款碼

請?jiān)谥黝}配置——文章設(shè)置里上傳

掃描二維碼手機(jī)訪問

文章目錄