C++異常

對於每位編程人員來說,程序的編譯或運行錯誤是很常見的,今天我在這裏粗略的總結一下C++中的異常

1、錯誤處理技術:

傳統的錯誤處理辦法:

(1)終止程序(如段錯誤等)

(2)返回錯誤碼

(3)返回和法值,讓程序出於某種非法的狀態。(太坑了)

(4)調用一個預先設置的出現錯誤是調用的函數。——回調函數

2、異常的處理

當一個函數發現自己無法處理的錯誤是拋出異常,讓函數的調用者直接或間接的處理這個問題

3、異常的拋出或捕獲

(1)異常是通過拋出對象而引發的,該對象的類型決定了應該激活哪個處理代碼。

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

(3)拋出異常後釋放後會釋放局部存儲對象,所以被拋出的對象也還會還給系統,throw表達式會初始化一個拋出特殊的異常對象副本(匿名對象),異常對象由編譯器管理,異常對象在傳給對應的catch處理之後撤銷。

4、棧展開

     異常的時候,將暫停當前函數的執行,開始查找對應的匹配catch子句。

     檢查throw本身是否在try塊內部,如果是在查找匹配的catch語句。

    有匹配的,則處理。沒有則退出當前函數棧,繼續再調用函數的棧中進行查找。

     重複上述過程。若到達main函數的棧,依舊沒有匹配的,則終止程序。

      這個沿着調用鏈查找匹配的catch子句的過程稱爲棧展開。

      匹配的catch子句並處理以後,會繼續沿着catch子句後面繼續執行。

5、異常捕獲的匹配規則

異常對象的類型與catch說明符的類型必須完全匹配。但以下情況例外:

(1)允許從非const對象到const對象的轉換。

(2)允許從派生類型到基類類型的轉換。

(3)將數組轉換爲指向數組類型的指針,將函數轉換爲指向函數類型的指針。

#include<iostream>
#include<string>
using namespace std;
class Excption
{
public:
 Excption(int errId,const char* errMsg)
  :_errId(errId)
  ,_errMsg(errMsg)
 {}
 void What() const
 {
  cout<<"errId:"<<_errId<<endl;
  cout<<"errMsg:"<<_errMsg<<endl;
 }
private:
 int _errId;//錯誤碼
 string _errMsg;//錯誤消息
};

void Func1(bool isThrow)
{
 if(isThrow)
 {
    throw Excption(1,"拋出Excption對象");
 }
 printf("Func1(%d)\n",isThrow);
}
void Func2(bool isThrowString,bool isThrowInt)
{
 if(isThrowString)
 {
  throw string("拋出string對象");
 }
 if(isThrowInt)
 {
  throw 2;
 }
 printf("Func2(%d,%d)\n",isThrowString,isThrowInt);
}
void Func()
{
 try
 {
  Func1(false);
  Func2(true,true);
 }
 catch(const string& errMsg)
 {
  cout<<"Ctach string Object:"<<errMsg<<endl;
 }
 catch(int errId)
 {
  cout<<"Catch int Object:"<<errId<<endl;
 }
 catch(const Excption& e)
 {
  e.What();
 }
 catch(...)
 {
  cout<<"未知異常"<<endl;
 }
 printf("Func()\n");
}
int main()
{
  Func();

 return 0;
}

       異常的重新拋出

      有可能單個的catch不能讓=完全處理一個異常,在運行一些校正處理以後,希望再交給更外層的調用鏈函數來處理,catch則可以通過重新拋出異常傳遞給更上層的函數進行處理。

class Excption
{
public:
 Excption(int errId=0,const char* errMsg="")
  :_errId(errId)
  ,_errMsg(errMsg)
 {}
 void What() const
 {
  cout<<"errId:"<<_errId<<endl;
  cout<<"errMsg:"<<_errMsg<<endl;
 }
private:
 int _errId;//錯誤碼
 string _errMsg;//錯誤消息
};

void Func1()
{
 throw string("Throw Func1 string");
}
void Func2()
{
 try
 {
  Func1();
 }
 catch(string& errMsg)
 {
  cout<<errMsg<<endl;
 }
}
void Func3()
{
 try
 {
  Func2();
  }
 catch(Excption& e

 {
  e.What();

 }
}
int main()
{
 Func3();
 return 0;
}

6、異常與構造函數&析構函數

(1)構造函數完成對象的構造和初始化,需要保證不要在構造函數中拋出異常,否則可能導致對象不完整或沒有初始化。

(2)析構函數主要完成資源的清理,需要保證不要在析構函數中拋出異常,否則可能導致資源泄漏

7、C++標準庫定義的excetion類

class Exception1:public exception
{
public:
 Exception1(int errId=0,const char* errMsg="")
  :_errId(errId)
  ,_errMsg(errMsg)
 {}
  virtual const char* What() const
 {
  cout<<"errId:"<<_errId<<endl;
  cout<<"errMsg:"<<_errMsg<<endl;
  return _errMsg.c_str();
 }
private:
 int _errId;//錯誤碼
 string _errMsg;//錯誤消息
};
class Exception2:public exception
{
public:
 Exception2(int errId=0,const char* errMsg="")
  :_errId(errId)
  ,_errMsg(errMsg)
 {}
 virtual const char*what() const
 {
  cout<<"errId:"<<_errId<<endl;
  cout<<"errMsg:"<<_errMsg<<endl;
  return _errMsg.c_str();
 }
private:
 int _errId;//錯誤碼
 string _errMsg;//錯誤消息
};

void Func()
{
 try
 {
  throw Exception1(1,"Exception1");
  throw Exception2(2,"Exception2");
 }
 catch(exception& e)
 {
  e.what();
 }
}
void Func1()
{
 try
 {
  int *p=new int[0x7fffffff/4];
 }
 catch(std::bad_alloc&ba)
 {
  std::cout<<"bad_alloc caught:"<<ba.what()<<"\n";
 }
 catch(exception& e)
 {
  std::cout<<"bad_alloc caught:"<<e.what()<<"\n";
 }
}
int main()
{
 Func();
 Func1();
   return 0;
}

 

excption類是C++定義的一個標準異常的類,通常我們通過繼承二線產品提exception類定義合適的異常類。

8、使用異常和使用返回錯誤碼相比,優缺點是什麼

(1)異常捕獲後可清楚知道發生了什麼錯誤,錯誤碼只是整型,不能描述錯誤信息

(2)異常會打亂執行流,會影響調試分析代碼

(3)異常存在安全問題,須儘量使用RAII配合

(4)許多C++第三方庫(比如boost)使用異常,關閉異常將難以與之結合

(5)很多測試框架使用異常gtest/gmock--ut

發佈了53 篇原創文章 · 獲贊 3 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章