C++異常處理寫的代碼太丑怎麼辦?
雷同問題:https://www.zhihu.com/question/22889420
問題已解決,感謝大家的指導!!萬分感謝
//////////
本人初次使用異常處理,但是寫出來的代碼感覺一點都不清晰,請教一下大家,有沒有辦法解決
這個不是C++的鍋,按你的寫法到Java上也是一樣混亂。
try-catch的首要原則是只catch你能處理且你想處理的異常。
bad_alloc就是典型的絕大多數情況下沒法處理的異常。(其實你會不會收到這個異常都難說)
次要原則是劃定try的範圍,合併相同的處理方式。如果你不管遇到什麼錯誤都是返回一個空string,那在函數最外層包一個try catch不就行了。
此外我覺得你對C++的理解是有問題:
- 最裡面的try{return}catch很可能是沒意義的,這個異常很可能是在函數返回後拋的,你這裡接不住。
- 先new char再遞給string的做法多此一舉,string resize就行了,而且你new完也沒檢查是不是nullptr。(nullptr這個錯的,一時腦抽想成malloc了)
最後還有一個問題:既然你把異常都catch住了,那調用方看見返回一個空字元串時,怎麼知道是哪裡錯了呢?
如果你不打算讓調用方看到/知道發生了錯誤,那除了try catch外,不如再加個noexcept。
寫成這樣的最大原因其實是你在試圖手工管理內存,請用類似 std::vector 的東西。
另外,無論是否使用異常,大多數情況下你的需求都是統一處理「出錯」情況。除非你有針對「內存分配出錯」的特殊處理需求,否則不要 catch bad_alloc
。
比如你想實現 UTF16ToUTF8
這個函數不拋出任何異常,發生任何出錯情況都返回空字元串的話,在函數層面 try
一下就足夠了。例如:
std::string UTF16ToUTF8(wchar_t const *pWideBytes, int cchChar) noexcept
try {
int const count = WideCharToMultiByte(CP_UTF8, 0, pWideBytes,
cchChar, NULL, 0, NULL, NULL);
if (count == 0) {
return {};
}
std::vector& 當然,返回空字元串並不是好的錯誤處理方法,因為空字元串也可以作為參數,調用方甚至沒法根據返回值是否為空得知是否出錯。 好的處理方法是在這個函數里根本不要 try catch。原因並不是我們常聽說的「內存不足就應該直接崩潰」,而是這裡的出錯情況應該通知並交給外部處理,到底是崩潰還是有其它處理方案,你在當前這個函數里是不應該知道的。 // 錯誤作為輸出參數 // 錯誤作為返回值,結果作為輸出參數 // 奇行種 反正我選第一種。
if (WideCharToMultiByte(CP_UTF8, 0, pWideBytes, cchChar,
buffer.data(), buffer.size(), NULL, NULL) &< 0)
{
return {};
}
return {std::begin(buffer), std::end(buffer)};
}
catch (std::exception const) {
return {};
}
// 使用異常
std::string UTF16ToUTF8(wchar_t const *pWideBytes, int cchChar);
std::string UTF16ToUTF8(wchar_t const *pWideBytes, int cchChar, Error err) noexcept;
Error UTF16ToUTF8(wchar_t const *pWideBytes, int cchChar, std::string out) noexcept;
void UTF16ToUTF8(wchar_t const *pWideBytes, int cchChar, std::string out, Error err) noexcept;
先new再複製給std::string是什麼鬼才操作。
int nUtf8Count = WideCharToMultiByte(CP_UTF8, 0, pWideBytes, cchChar, nullptr, 0, nullptr, nullptr);
if (nUtf8Count &<= 0) {
throw std::system_error(GetLastError(), std::system_category(), __func__);
}
std::string s;
s.resize(nUtf8Count);
if (WideCharToMultiByte(CP_UTF8, 0, pWideBytes, cchChar, s.data(), nUtf8Count, nullptr, nullptr) &<= 0) {
throw std::system_error(GetLastError(), std::system_category(), __func__);
}
return s;
想返回空字元串就在外麵包上try { /* 原來內容 */ } catch (...) { return std::string(); }
,不過個人不建議,因為空字元串是正常值。
~~~~
即使古代std::string不能直接用data(),也不需要手動管理內存啊。既然會用std::string會catch,應該也學過寫RAII包裝了吧。
int nUtf8Count = WideCharToMultiByte(CP_UTF8, 0, pWideBytes, cchChar, NULL, 0, NULL, NULL);
if (nUtf8Count &<= 0) {
throw system_error(GetLastError(), system_category());
}
struct RAIIBuffer { // unique_ptr&
RAIIBuffer(char* ptr) { this-&>ptr = ptr; }
char* get() { return ptr; }
~RAIIBuffer() { delete[] ptr; }
private:
char* ptr;
RAIIBuffer(const RAIIBuffer);
void operator=(const RAIIBuffer);
};
RAIIBuffer buf(new char[nUtf8Count]);
if (WideCharToMultiByte(CP_UTF8, 0, pWideBytes, cchChar, buf.get(), nUtf8Count, NULL, NULL) &<= 0) {
throw system_error(GetLastError(), system_category());
}
return std::string(buf.get(), nUtf8Count);
內存不足,程序應該直接崩潰,繼續運行會出現更多錯誤。
所以,對內存不足的最佳應對就是不要捕捉異常,直接讓程序掛掉。
沒有異常機制之前,我們需要檢查內存分配的返回值,就是怕內存出錯之後繼續運行,需要手工捕捉內存分配失敗儘早退出應用。有異常之後程序機制直接幫你退出進程了,你就別多事了。
現代應用的理念是:不怕掛,就怕卡。伺服器掛了自然有熱備的伺服器頂上,桌面掛了用戶自然會重啟。
但是內存不足的時候基本就是交換空間也滿了,整個系統響應極慢,只有退出大量程序才能解決。這種時候你非要保證程序運行不退出,那你這個應用不是跟毒瘤似的么。
就算你的程序是嵌入式,需要保證常開,常用的方法也是用看門狗監控它掛掉就立即重啟,而不是不讓它崩潰。
--
退一萬步說,如果你的程序真的不能退出,那麼你要解決的也是不讓內存不夠這件事情發生。而不是在發生之後假裝當做不知道。
異常處理兩個原則,已知,可解決。
你這裡的問題是違背第二個原則,可解決。內存不夠是你程序無法解決的問題,所以不應該捕獲異常。
什麼異常是可解決的?例如你打開一個文件失敗了,你可以過一會兒再試一次,說不定就成功了,這就是可解決的。
那既然內存溢出是無法解決的問題,為什麼要設計這個異常呢?
因為有人可以解決。
譬如說宿主或者應用程序,他們提示給用戶或者記錄個日誌,內存溢出導致程序退出就完了……
推薦閱讀: