2010-02-01 94 views
3

在關閉正在寫入的文件時檢測錯誤很重要,因爲在關閉期間數據的最後一部分可能會被刷新,如果丟失,那麼最後一次寫入失敗並且有人應該知道它。文件對象的析構函數是一個很好的自動關閉它的地方,但人們說不要從析構函數中拋出異常,所以如果關閉失敗,那麼你怎麼知道它?如何處理拋出異常的文件析構函數?

我聽說有人建議手動調用文件的close()方法。這聽起來不錯,但如果多個文件的close()方法都無法在這樣的情況下,會發生什麼:

MyFile x(0), y(1), z(2); 
    x.close(); 
    y.close(); 
    z.close(); 

嗯,看來,如果的close()方法「X」拋出一個異常,那麼你已經做得很好,堅持規則,以避免「x」的析構函數拋出異常,但現在你是好 - 對'y'和'z'的close()方法的早期調用不會執行直到它們的析構函數。那麼,在'y'的析構函數中調用'y'的close()方法或在'z'的析構函數中調用'z'的close()方法時,如果它們拋出異常,那麼你'被擰緊了。

有沒有一種合理的方法不會在這種情況下被搞砸?

+2

看起來可能是C++,相應標記 – skaffman 2010-02-01 15:50:25

+0

我在這裏沒有看到問題。析構函數必須包含一個關閉調用,必然被try-catch包圍。在析構函數中處理失敗不能通過異常,句點來完成。 – 2010-07-09 09:04:37

回答

5

是的,捕獲從析構函數中關閉拋出的異常。

這是極其重要重要的是一個C++析構函數不會拋出異常期。否則將會在幾乎每個可用的庫中混淆許多資源管理例程。

確實如此,在這種情況下,您會通過捕獲異常而失去失敗信息。如果用戶確實擔心錯誤,他們可以手動調用close並處理異常。

2

這是一個FAQ項:17.3 How can I handle a destructor that fails?

編輯:

好吧,看來,如果close()方法的 方法 'X' 拋出一個異常,那麼 你已經做得很好保持規則爲 避免在'x'的 析構函數中拋出異常,除非現在您是 好心地早期調用 close()方法的'y'和'z'不會執行到 直到它們析構函數。當堆棧解開只要你有周圍的MyFile ... z.close()部分安裝了try-catch

yz號Dtors將被稱爲。更好的主意是將close也放在Dtor中。 x的驅動程序將不會被執行 - 因此在catch區塊中需要進行一些清理。

我建議你運行下面的程序,以更好地瞭解一個異常的情況下,析構函數,調用(一次通過取消註釋S x線,是又一次):

#include <iostream> 

using namespace std; 

struct s { 
s(int i = 0) : m_i(i) { cout << __func__ << endl; } 
~s() { if (m_i == 0) except(); cout << __func__ << endl; } 
void except() { throw 42; } 
int m_i; 
}; 

int main() { 
    try 
    { 
     s y(2), z(3); 
     /* s x */ 
     y.except(); 
    } 
    catch (...) { cout << "exception\n"; } 

    cout << "stack unwound\n"; 
} 
1

你不應該從拋析構函數 - 這樣:

如果關閉了拋出的異常來電,我會吞下他們,請執行下列操作之一:

選項1:寫出一個錯誤信息,並中止程序。

選項2:通過一個包裝器對象(我可能會這樣做)或全局變量或(最好)一個線程本地內存中的變量來使錯誤可用。

選項1和2在這裏看起來都很合理。

對於選項2和包裝,你會怎麼做:

WrapFileX.close(); 
WrapFileY.close(); 
WrapFileZ.close(); 

if(WrapFileX.hasError || WrapFileY.hasError || WrapfileZ.hasError) 
{ //log 
    exit(1) 
} 
1

在這個例子中,我不明白爲什麼什麼應該被拋出。我認爲這種情況根本不值得例外。理論上,關閉並沒有失敗,它只是沒有寫出其餘的緩衝區;這不是一個非常特殊的情況。它可以被處理,並且應該被處理,除非有理由需要關閉該文件這個即時

我個人將只有我的close()函數塊,直到寫入完成,然後繼續關閉。

+1

flush/close也可能永久失敗,例如,如果底層文件系統空間不足,或者底層套接字或管道已斷開連接或中斷。不影響你的主要觀點,一個例外是沒有必要的,但你不能等到它工作。 – 2010-02-01 19:56:11

0
try { 
x.close(); 
y.close(); 
z.close(); 
} 
catch { 
//do what ever you need to do here, then close() what' you'll need to close here 
} 

這就是我會做,問題是你可能不知道哪一個拋出的異常,和一個離開封閉。

1

,因爲我看到它是規則:

如果你不關心異常然後讓析構函數做近距離工作(和捕獲並丟棄例外)。如果你關心(並且可以做些什麼),然後手動關閉並捕獲異常並做適當的工作。

{ 
    std::fstream X("Plop_X"); 
    std::fstream Y("Plop_Y"); 
    std::fstream Z("Plop_Z"); 

    // Do work 

    try 
    { 
     X.close(); 
    } 
    catch(Plop const& e) 
    { 
     // Fix the exception 
     // Then make sure X closes correctly. 

     // If we can't fix the problem 
     // rethrow 
     if (badJuJu) 
     { throw; 
     } 
    } 
    // Don't care about Y/Z let the destructor close them and discard the exception. 
} 
0

首先的,MyFile的析構函數應該捕獲異常(這是一個令人難以置信的強大的「應當」 - 這不是「必須」,因爲行爲,如果它沒有被明確定義,但它幾乎從來沒有期望的):

~MyFile() { 
    try { 
     close(); 
    } catch(...) {} 
    // other cleanup 
} 

接下來,調用者應該決定他們是否想要處理錯誤。如果他們不這樣做,那麼他們可以讓析構函數被調用。如果他們這樣做,那麼他們必須自己關閉。如果您的三個文件,假設一旦他們中的一個出現故障,你不關心別人例如,你可以這樣做:

MyFile x(0), y(1), z(2); 
try { 
    x.close(); 
    y.close(); 
    z.close(); 
} catch(...) { 
    std::cerr << "something failed to close\n"; 
} 

如果你想知道哪些失敗了,你需要確保所有三個關閉功能實際上被稱爲:

MyFile x(0), y(1), z(2); 
try { 
    x.close(); 
} catch(...) { 
    std::cerr << "x failed to close\n"; 
} 
try { 
    y.close(); 
} catch(...) { 
    std::cerr << "y failed to close\n"; 
} 
try { 
    z.close(); 
} catch(...) { 
    std::cerr << "z failed to close\n"; 
} 

當然,你可能想要共同的代碼了一下。另外,如果你知道close可以拋出的一切,那麼你可以有一個比(...)更好的catch子句。

這可能是爲什麼標準庫中的流的close()函數不會拋出異常,除非用戶設置了異常掩碼已啓用該行爲。

相關問題