根據下面的聲明,寫出實現的代碼,要求達到異常安全或異常中立。這意味著即使發生了異常,Stack對象也要處於正確統一的狀態。異常要被傳遞給調用者,讓調用者根據上下文處理異常。
template <class T><br />class Stack<br />{<br />public:<br />Stack();<br />~Stack();<br />/*...*/<br />private:<br />T* v_; // 棧的緩衝區<br />size_t vsize_; // 棧的容量<br />size_t vused_; // 緩衝區中實際元素個數<br />};
請思考下面的問題:
● 異常安全的層級有哪些?
● 通用容器是否可以做到“異常中立”(exception neutral)?
● STL裡的容器是異常安全,還是異常中立?
● 異常安全是否會影響容器的介面設計?
● 通用容器是否應該有異常列表?
template <class T><br />Stack<T>::Stack()<br />: v_(0), vsize_(10), vused_(0)<br />{<br />v_ = new T[vsize_];<br />}
上面的ctor是異常安全還是異常中立?
假設任意函數都可以拋出異常,那麼先要知道都調用了哪些函數。new T[vsize_] 這句先調用了operator new[]()(全域或T的成員函數)申請記憶體,然後調用vsize_個T的ctor來初始化這些對象。
1> operator new[]()失敗時會拋出bad_alloc異常;
2> T的ctor也可以拋出異常,然後系統自動調用dtor把前面構造成功的對象都銷毀,最後回收所有記憶體。
3> 在dtor銷毀對象時如果拋出異常,那麼系統直接調用terminate中止整個應用程式的執行,更乾淨。
所以,Stack::Stack()既是異常安全也是異常中立。
1> 如果拋出了bad_alloc異常,則該異常被傳遞到調用者那裡,而沒有被本地捕獲。這就做到了異常中立。
2> 無論何種異常,最後記憶體都被回收。這就做到了資源無泄漏。
3> 對象狀態能在異常前後保持正確。vsize_雖然被賦值,但是因為對象最後都被銷毀了,所以相當於不存在錯誤對象。
template <class T><br />Stack<T>::Stack()<br />: v_(new T[10]), vsize_(10), vused_(0)<br />{}
這樣寫和上面沒什麼區別。
再看dtor的實現:
template<class T><br />Stack<T>::~Stack()<br />{<br />delete[] v_; // this can't throw<br />}
那句delete實際上調用了T::~T()和operator delete[]()。後者因為聲明的關係不拋出異常:
void operator delete[]( void* ) throw();
void operator delete[]( void*, size_t ) throw();
如果有人重載了它們而拋出了異常,則認為它們犯錯!
~T()可能拋出異常,但Stack要求它不拋出。否則,讓dtor做到完全的異常安全是不可能的。
換言之,一個類的dtor如果允許拋出異常,那麼該類不可能正常工作!保證異常不離開dtor是很重要的。如果你習慣上給每個dtor後面都加上throw(),沒問題。這說明你重視這方面。