1) 函數後面聲明 throw() 只是接口的提供者和接口的使用者間的默契或稱協議。
2) 這種協議不影響正常的異常處理流程。
throw()表示此函數不會拋出異常,函數後面可以跟上 throw( int ),表示該函數可能會拋出 int型的異常。但不會拋出別的類型的異常。使用者應該注意捕獲 該函數可能拋出的int型的異常
C++的異常類是沒有棧痕跡的,如果需要獲取棧痕跡,需要使用以下函數:
#include <execinfo.h> int backtrace(void **buffer, int size); char **backtrace_symbols(void *const *buffer, int size); void backtrace_symbols_fd(void *const *buffer, int size, int fd);
backtrace將當前程序的調用信息存儲在buffer中,backtrace_symbols則是將buffer翻譯爲字符串。後者用到了malloc,所以需要手工釋放內存。
man手冊中提供瞭如下的代碼:
#include <execinfo.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> void myfunc3(void) { int j, nptrs; #define SIZE 100 void *buffer[100]; char **strings; nptrs = backtrace(buffer, SIZE); printf("backtrace() returned %d addresses\n", nptrs); /* The call backtrace_symbols_fd(buffer, nptrs, STDOUT_FILENO) would produce similar output to the following: */ strings = backtrace_symbols(buffer, nptrs); if (strings == NULL) { perror("backtrace_symbols"); exit(EXIT_FAILURE); } for (j = 0; j < nptrs; j++) printf("%s\n", strings[j]); free(strings); } static void /* "static" means don't export the symbol... */ myfunc2(void) { myfunc3(); } void myfunc(int ncalls) { if (ncalls > 1) myfunc(ncalls - 1); else myfunc2(); } int main(int argc, char *argv[]) { if (argc != 2) { fprintf(stderr, "%s num-calls\n", argv[0]); exit(EXIT_FAILURE); } myfunc(atoi(argv[1])); exit(EXIT_SUCCESS); }
編譯並執行:
$ cc -rdynamic prog.c -o prog $ ./prog 3
輸出如下:
backtrace() returned 8 addresses ./prog(myfunc3+0x1f) [0x8048783] ./prog() [0x8048810] ./prog(myfunc+0x21) [0x8048833] ./prog(myfunc+0x1a) [0x804882c] ./prog(myfunc+0x1a) [0x804882c] ./prog(main+0x52) [0x8048887] /lib/i386-linux-gnu/libc.so.6(__libc_start_main+0xf3) [0xb76174d3] ./prog() [0x80486d1]
因此我寫出以下的異常類,注意上面的打印結果經過了名字改編,所以我們使用abi::__cxa_demangle將名字還原出來。
Exception.h
#ifndef EXCEPTION_H_ #define EXCEPTION_H_ #include <string> #include <exception> class Exception : public std::exception { public: explicit Exception(const char* what); explicit Exception(const std::string& what); virtual ~Exception() throw(); virtual const char* what() const throw(); const char* stackTrace() const throw(); private: void fillStackTrace(); //填充棧痕跡 std::string demangle(const char* symbol); //反名字改編 std::string message_; //異常信息 std::string stack_; //棧trace }; #endif // EXCEPTION_H_
Exception.cpp
#include "Exception.h" #include <cxxabi.h> #include <execinfo.h> #include <stdlib.h> #include <stdio.h> using namespace std; Exception::Exception(const char* msg) : message_(msg) { fillStackTrace(); } Exception::Exception(const string& msg) : message_(msg) { fillStackTrace(); } Exception::~Exception() throw () { } const char* Exception::what() const throw() { return message_.c_str(); } const char* Exception::stackTrace() const throw() { return stack_.c_str(); } //填充棧痕跡 void Exception::fillStackTrace() { const int len = 200; void* buffer[len]; int nptrs = ::backtrace(buffer, len); //列出當前函數調用關係 //將從backtrace函數獲取的信息轉化爲一個字符串數組 char** strings = ::backtrace_symbols(buffer, nptrs); if (strings) { for (int i = 0; i < nptrs; ++i) { // TODO demangle funcion name with abi::__cxa_demangle //strings[i]代表某一層的調用痕跡 stack_.append(demangle(strings[i])); stack_.push_back('\n'); } free(strings); } } //反名字改編 string Exception::demangle(const char* symbol) { size_t size; int status; char temp[128]; char* demangled; //first, try to demangle a c++ name if (1 == sscanf(symbol, "%*[^(]%*[^_]%127[^)+]", temp)) { if (NULL != (demangled = abi::__cxa_demangle(temp, NULL, &size, &status))) { string result(demangled); free(demangled); return result; } } //if that didn't work, try to get a regular c symbol if (1 == sscanf(symbol, "%127s", temp)) { return temp; } //if all else fails, just return the symbol return symbol; }
測試代碼如下:
#include "Exception.h" #include <stdio.h> using namespace std; class Bar { public: void test() { throw Exception("oops"); } }; void foo() { Bar b; b.test(); } int main() { try { foo(); } catch (const Exception& ex) { printf("reason: %s\n", ex.what()); printf("stack trace: %s\n", ex.stackTrace()); } }
打印結果如下:
reason: oops stack trace: Exception::fillStackTrace() Exception::Exception(char const*) Bar::test() foo() ./a.out(main+0xf) /lib/i386-linux-gnu/libc.so.6(__libc_start_main+0xf3) ./a.out()
注意編譯的時候,加上-rdynamic選項
有了這個類,我們可以在程序中這樣處理異常:
try { // } catch (const Exception& ex) { fprintf(stderr, "reason: %s\n", ex.what()); fprintf(stderr, "stack trace: %s\n", ex.stackTrace()); abort(); } catch (const std::exception& ex) { fprintf(stderr, "reason: %s\n", ex.what()); abort(); } catch (...) { fprintf(stderr, "unknown exception caught \n"); throw; // rethrow }