我正在撰寫一個以 astd::istream作為引數并從中讀取以解碼影像的 C 函式。解碼影像時,如果在讀取程序中發生錯誤,我希望流拋出例外。這樣,我就不必在其余的解碼邏輯中穿插檢查錯誤標志,從而使我的代碼更簡單。
因為呼叫者可能會在未啟用例外的情況下傳遞流,所以我的函式將啟用它們。我想在函式退出時恢復流的初始例外掩碼。我只是在學習 C ,并試圖用慣用的方式來處理這樣的情況——我通常會在一個finally塊中扔掉清理代碼:
Image::Image(std::istream& stream) {
std::ios_base::iostate originalExceptionSettings = stream.exceptions();
try {
// Enable exceptions.
stream.exceptions(
std::ios_base::badbit |
std::ios_base::failbit |
std::ios_base::eofbit);
// Do some stuff that might cause stream to throw...
}
finally { // If only!
// Restore settings that the caller expects.
stream.exceptions(originalExceptionSettings);
}
}
我見過有人說finally-type 清理代碼應該像 RAII 一樣處理。我想我可以創建一個小類來包裝這個職責,它保存指向流的指標和原始例外掩碼。它的解構式會將原始例外掩碼恢復到流中。
// Something like this...
StreamExceptionMaskMemo::StreamExceptionMaskMemo(std::istream* stream)
{
this->originalExceptionSettings = stream->exceptions();
this->stream = stream;
}
StreamExceptionMaskMemo::~StreamExceptionMaskMemo()
{
// Could throw!
this->stream->exceptions(this->originalExceptionSettings);
}
雖然這在我的情況下會起作用,但使用此類來存盤指定應拋出例外以在禁用例外的函式中使用的例外掩碼是有問題的。如果以這種方式使用該類,則解構式將重新啟用例外,這會立即拋出當前狀態所暗示的任何例外。
我意識到這有點做作——可能呼叫者不會再使用混亂的流了——但我仍然對如何解決以下問題感到困惑:
- 如何撰寫使用呼叫者提供的流的代碼,知道流可能具有完全不同的行為,具體取決于例外掩碼是什么?
- 你如何撰寫可能拋出的清理代碼?如果我使用 a
finally我可以捕獲從finally塊內部拋出的例外并做一些適當的事情。但是,如果我將清理的責任分拆給另一個類以允許 RAII,我必須讓它的解構式拋出例外,或者吞下它們。
uj5u.com熱心網友回復:
如果呼叫者不希望流例外,則舊掩碼不會啟用例外,因此舊掩碼的恢復不會拋出任何例外。不是問題。
如果呼叫者確實想要流例外,那么如果流狀態與呼叫者想要的例外匹配,則恢復將拋出例外。再次,不是問題。
因此,唯一真正的問題是從 RAII 解構式內部拋出例外的可能性,這通常是非常糟糕的(但可以小心完成)。
在這種情況下,我建議不要使用 RAII。一個簡單的try/catch就足夠了(try/finally不可移植),例如:
Image::Image(std::istream& stream) {
std::ios_base::iostate originalExceptionSettings = stream.exceptions();
try {
// Enable exceptions (may throw immediately!)
stream.exceptions(
std::ios_base::badbit |
std::ios_base::failbit |
std::ios_base::eofbit);
// Do some stuff that might cause stream to throw...
}
catch (const std::exception &ex) {
// error handling as needed...
// if not a stream error, restore settings that
// the caller expects, then re-throw the original error
if (!dynamic_cast<const std::ios_base::failure*>(&ex))
{
stream.exceptions(originalExceptionSettings);
throw;
}
}
catch (...) {
// error handling as needed...
// Unknown error but not a stream error, restore settings
// that the caller expects, then re-throw the original error
stream.exceptions(originalExceptionSettings);
throw;
}
// restore settings that the caller expects (may throw what caller wants!)
stream.exceptions(originalExceptionSettings);
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/qianduan/461187.html
