日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程语言 > c/c++ >内容正文

c/c++

如何写一个完善的c++异常处理类

發(fā)布時(shí)間:2025/3/21 c/c++ 23 豆豆
生活随笔 收集整理的這篇文章主要介紹了 如何写一个完善的c++异常处理类 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

我們的異常處理類的features

如何寫一個(gè)異常處理類是一個(gè)不太容易的事情,最近剛好接觸了一些不錯(cuò)的代碼,看到了一些技巧,這里和大家分享一下。

一個(gè)相對(duì)完善的異常處理類(以及附加的一些東西)應(yīng)該能夠處理下面的一些功能:

1) 能夠方便的定義異常類的繼承樹

2) 能夠方便的throw、catch,也就是在代碼中捕獲、處理代碼的部分應(yīng)該更短

3) 能夠獲取異常出現(xiàn)的源文件的名字、方法的名字、行號(hào)

4) 能夠獲取異常出現(xiàn)的調(diào)用棧并且打印出來

由于目前我用的平臺(tái)是linux,所以里面調(diào)用的一些函數(shù)也只是在linux下面有用。Windows也肯定是具有相應(yīng)的函數(shù)的,具體可能需要去查查

首先科普一些內(nèi)容:

1) 對(duì)于沒有捕獲的異常(no handler),則會(huì)終止程序,調(diào)用terminate()

2) 在定義函數(shù)的時(shí)候,我們可以在定義的后面加上throw (exception1, exception2…):

??? a) 如果沒有寫這一段、則可能拋出任意的異常

??? b) 如果寫throw(),則表示函數(shù)不能拋出任意的異常

??? c) 如果寫throw(A, B), 表示函數(shù)拋出A、B的異常

如果拋出的異常不在列表范圍內(nèi),則異常不能被catch,也就會(huì)調(diào)用terminate()

我們構(gòu)想一下我們定義、調(diào)用我們的異常類的時(shí)候是怎樣的一個(gè)情形:

1) 定義:

1: class DerivedException : public BaseException 2: { 3: public: 4: MY_DEFINE_EXCEPTION(DerivedException, BaseException); 5: };

2) 如何拋出異常

1: MY_THROW(DerivedException)

3) 如何catch異常

1: catch (DerivedException& e) 2: { 3: cout<< e.what() << endl; 4: }

這個(gè)輸出的內(nèi)容包括錯(cuò)誤的行號(hào)、文件名、方法名、和調(diào)用棧的列表

給出我們異常類的頭文件:

1: #ifndef EXCEPTION_TEST 2: #define EXCEPTION_TEST 3:? 4: #include <exception> 5: #include <string> 6:? 7: #define MY_THROW(ExClass, args...) \ 8: do \ 9: { \ 10: ExClass e(args); \ 11: e.Init(__FILE__, __PRETTY_FUNCTION__, __LINE__); \ 12: throw e; \ 13: } \ 14: while (false) 15:? 16: #define MY_DEFINE_EXCEPTION(ExClass, Base) \ 17: ExClass(const std::string& msg = "") throw() \ 18: : Base(msg) \ 19: {} \ 20: \ 21: ~ExClass() throw() {} \ 22: \ 23: /* override */ std::string GetClassName() const \ 24: { \ 25: return #ExClass; \ 26: } 27:? 28: class ExceptionBase : public std::exception 29: { 30: public: 31: ExceptionBase(const std::string& msg = "") throw(); 32:? 33: virtual ~ExceptionBase() throw(); 34:? 35: void Init(const char* file, const char* func, int line); 36:? 37: virtual std::string GetClassName() const; 38:? 39: virtual std::string GetMessage() const; 40:? 41: const char* what() const throw(); 42:? 43: const std::string& ToString() const; 44:? 45: std::string GetStackTrace() const; 46:? 47: protected: 48: std::string mMsg; 49: const char* mFile; 50: const char* mFunc; 51: int mLine; 52:? 53: private: 54: enum { MAX_STACK_TRACE_SIZE = 50 }; 55: void* mStackTrace[MAX_STACK_TRACE_SIZE]; 56: size_t mStackTraceSize; 57: mutable std::string mWhat; 58: }; 59:? 60: class ExceptionDerived : public ExceptionBase 61: { 62: public: 63: MY_DEFINE_EXCEPTION(ExceptionDerived, ExceptionBase); 64: }; 65:? 66: #endif

這個(gè)頭文件首先定義了兩個(gè)宏,這里先暫時(shí)不管他,我先來解釋一下ExceptionBase,它繼承自std::exception,std::exception里面其實(shí)已經(jīng)提供了一些功能了,但是比較弱,為了實(shí)現(xiàn)我們上文提到的功能,這里只是繼承了std:exception的借口,也就是what()函數(shù)。

上面的接口應(yīng)該比較好理解,45行的GetStackTrace是打印當(dāng)前的調(diào)用棧,49-51行分別存儲(chǔ)了當(dāng)前出現(xiàn)exception的源文件名,函數(shù)名,行號(hào),54行定義了最大的調(diào)用棧顯示的深度,也就是顯示50行。

60行顯示了怎樣定義一個(gè)新的異常類,這個(gè)就很方便了,通過MY_DEFINE_EXCEPTION宏去定義了一個(gè)繼承類,詳情見16行,這里不再細(xì)說,我這里想說說7行的MY_THROW宏,使用了3個(gè)內(nèi)置的參數(shù),__FILE__, __LINE__, __PRETTY_FUNCTION__, 他們分別是當(dāng)前的文件名,行號(hào),和函數(shù)名,他們的使用方法是在哪兒出現(xiàn),其相應(yīng)的值就是什么。

為什么這里要使用MY_THROW宏呢?其實(shí)是為了方便的把行號(hào)、文件名等加入進(jìn)來,宏展開的時(shí)候是在一行上的,這樣也使得行號(hào)與出錯(cuò)的行號(hào)保持一致,而且讓代碼更簡(jiǎn)單。

給出異常類的.cpp文件:

1: #include <execinfo.h> 2: #include <stdlib.h> 3: #include <cxxabi.h> 4:? 5: #include <iostream> 6: #include <sstream> 7:? 8: #include "exception_test.h" 9:? 10: using namespace std; 11:? 12: ExceptionBase::ExceptionBase(const std::string& msg) throw() 13: : mMsg(msg), 14: mFile("<unknown file>"), 15: mFunc("<unknown func>"), 16: mLine(-1), 17: mStackTraceSize(0) 18: {} 19:? 20: ExceptionBase::~ExceptionBase() throw() 21: {} 22:? 23: void ExceptionBase::Init(const char* file, const char* func, int line) 24: { 25: mFile = file; 26: mFunc = func; 27: mLine = line; 28: mStackTraceSize = backtrace(mStackTrace, MAX_STACK_TRACE_SIZE); 29: } 30:? 31: std::string ExceptionBase::GetClassName() const 32: { 33: return "ExceptionBase"; 34: } 35:? 36: const char* ExceptionBase::what() const throw() 37: { 38: return ToString().c_str(); 39: } 40:? 41: const std::string& ExceptionBase::ToString() const 42: { 43: if (mWhat.empty()) 44: { 45: stringstream sstr(""); 46: if (mLine > 0) 47: { 48: sstr << mFile << "(" << mLine << ")"; 49: } 50: sstr << ": " << GetClassName(); 51: if (!GetMessage().empty()) 52: { 53: sstr << ": " << GetMessage(); 54: } 55: sstr << "\nStack Trace:\n"; 56: sstr << GetStackTrace(); 57: mWhat = sstr.str(); 58: } 59: return mWhat; 60: } 61:? 62: std::string ExceptionBase::GetMessage() const 63: { 64: return mMsg; 65: } 66:? 67: std::string ExceptionBase::GetStackTrace() const 68: { 69: if (mStackTraceSize == 0) 70: return "<No stack trace>\n"; 71: char** strings = backtrace_symbols(mStackTrace, 10); 72: if (strings == NULL) // Since this is for debug only thus 73: // non-critical, don't throw an exception. 74: return "<Unknown error: backtrace_symbols returned NULL>\n"; 75:? 76: std::string result; 77: for (size_t i = 0; i < mStackTraceSize; ++i) 78: { 79: std::string mangledName = strings[i]; 80: std::string::size_type begin = mangledName.find('('); 81: std::string::size_type end = mangledName.find('+', begin); 82: if (begin == std::string::npos || end == std::string::npos) 83: { 84: result += mangledName; 85: result += '\n'; 86: continue; 87: } 88: ++begin; 89: int status; 90: char* s = abi::__cxa_demangle(mangledName.substr(begin, end-begin).c_str(), 91: NULL, 0, &status); 92: if (status != 0) 93: { 94: result += mangledName; 95: result += '\n'; 96: continue; 97: } 98: std::string demangledName(s); 99: free(s); 100: // Ignore ExceptionBase::Init so the top frame is the 101: // user's frame where this exception is thrown. 102: // 103: // Can't just ignore frame#0 because the compiler might 104: // inline ExceptionBase::Init. 105: result += mangledName.substr(0, begin); 106: result += demangledName; 107: result += mangledName.substr(end); 108: result += '\n'; 109: } 110: free(strings); 111: return result; 112: } 113:? 114: /* 115: * test-main 116: */ 117: int f2() 118: { 119: MY_THROW(ExceptionDerived, "f2 throw"); 120: } 121: void f1() 122: { 123: try 124: { 125: f2(); 126: } 127: catch (ExceptionDerived& e) 128: { 129: cout << e.what() << endl; 130: } 131: } 132: int main() 133: { 134: f1(); 135: }

這是函數(shù)的實(shí)現(xiàn)代碼,其他的都比較好理解,67行的GetStackTrace是相對(duì)復(fù)雜一點(diǎn)的,里面用backtrace函數(shù)去獲取了當(dāng)前調(diào)用棧的層數(shù),用backtrace_symbols去獲取當(dāng)前調(diào)用棧的符號(hào),而且__cxa_demangle函數(shù)的使用也值得去看看,這里不再細(xì)說了。

117行后展示了一個(gè)測(cè)試代碼,代碼雖然定義比較麻煩,不過使用還是很方便的:)。

from:?http://www.cnblogs.com/LeftNotEasy/archive/2010/10/30/1865364.html

總結(jié)

以上是生活随笔為你收集整理的如何写一个完善的c++异常处理类的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。