C 中,在資源釋放時必然有可能觸發異常的情況下,如何維持 RAII 語義?

時間 2021-05-08 21:37:10

1樓:Lee Revere

考慮的問題應該是如果析構函式出現了異常,你準備怎麼處理。如果有辦法處理,就try-catch。否則,就把程式改成可以處理。

2樓:

template

void destructor(Target *t) noexcept catch (std::exception &etodotemplate

std::shared_ptr make_shared_safe(Args &&...args)

class Foo

private:

int j;

};int main(int argc, char *argvauto p = make_shared_safe(5);

return 0;

}這樣你看湊合能用不。

3樓:

析構函式不能丟擲異常 != 析構函式不能處理異常

資源釋放不了,拋異常的時候用個try-catch接住,然後重新嘗試釋放資源。也可以假裝無事發生過,這個資源就洩露了,但是至少程式還在跑。不管你在析構函式裡怎麼做,不要從析構函式裡面往上拋就行。

4樓:saturnman

如果你的資源管理物件在構造和析構的時候都可能發生異常,那麼應該把構造和析構的任務全部移動到public成員函式中去,異常從成員函式中丟擲並在構造和析構函式中抓住並處理這些異常。維護物件構造成功狀態的flag。

同時既然構造和析構都移動到成員函式中去了,那麼一般你的物件應該支援失敗後重新嘗試構造或是重新嘗試析構,否則特別容易引發邏輯錯誤。

5樓:storyhare

唯一可能的情況:資源釋放時,有新的資源分配需求(從而觸發異常)。

其他任何情況,都是編碼錯誤。

所以結論很簡單,預分配(在資源申請時)。

6樓:「已登出」

《Effective C++》條款08:別讓異常逃離析構函式。

RAII不是說不能使用異常,而是在丟擲的時候就把異常處理了。

7樓:liuquan

題目有些迷:必然觸發異常就已經違反rall了,違反後再維持,邏輯上不是很理解。如果說目的是為了避免那個必然觸發的異常的話,那麼前面提到的引用計數或者singleton可以用來避免這種情況,即讓資源自我管理自己的生命週期,或者由乙個確定最後釋放的第三方物件來管理。

8樓:MaxwellGeng

既然丟擲異常,說明記憶體很大概率出問題,甚至已經鐵鐵的洩露了,這種情況下相當於絕症了,需要解決的是為什麼會異常以及析構裡怎麼給異常乙個解決方法,否則就無腦智慧型指標給續一口命,雖然可能會讓問題更複雜。。

9樓:

涉及到異常/RAII/多執行緒等等的問題,無腦使用智慧型指標(shared_ptr)即可解決大部分問題。而既然析構函式不能丟擲異常(不執行緒安全),那麼就只需要將釋放資源這個動作延遲到可以丟擲異常(執行緒安全)的時候進行就行了。

假如您希望釋放的資源控制代碼型別是HANDLE,使用FreeHandle函式進行釋放。類A需要持有這種資源,則如下定義A(預設析構即可):

class A;

我們現在希望在m_pHandle析構的時候觸發某種機制,將HANDLE加入某種容器來記錄這個控制代碼已經可以釋放了,然後我們在稍後再處理它。但是很遺憾,容器的插入操作不可避免的可能丟擲異常,所以我們應該在一開始就將該控制代碼的位置預留好。

我們有乙個這樣子的全域性陣列,可以用於訪問所有已經產生的HANDLE(不管應不應該被釋放):

// Storage_t儲存真正的HANDLE和類A中std::shared_ptr對應的弱指標(型別擦除成void即可)

using

Storage_t

=std

::pair

,std

::weak_ptr

>>;// 用來儲存的全域性變數

std::

vector

shared_ptr

>>g_vecHandleList

;PS:請先忽略這個恐怖的雙重智慧型指標巢狀pair定義

我們可以這樣子寫乙個能建立HANDLE的工廠函式:

std::

shared_ptr

>CreateHandlePtr

()然後將該函式得到的shared_ptr用於構造A。在A物件析構時,A::m_pHandle析構只會導致spLifespan的強引用計數歸零,進而引起構造spLifespan使用的int析構(而佔位int的析構是不可能丟擲的),此時真正的HANDLE仍未析構並可以通過g_vecHandleList訪問,而對應的weak_ptr弱引用在進行lock操作時因強引用失效而失敗。

因此我們可以寫乙個簡單地「GC」函式,在合適的時機來清理應該被統一消滅的HANDLE物件。

std::size_t FreeUnusedHandle多執行緒場合可能要加鎖(

for(std::shared_ptr< Storage_t > spStorage : g_vecHandleList) // shared_ptr拷貝不丟擲若弱引用為空(已經析構),或者可轉換為shared_ptr(HANDLE仍在使用if(!

spStorage->second || spStorage->second.lock()) // 不丟擲continue開始釋放這個控制代碼

FreeHandle(spStorage->first); // 按照題設可能丟擲任何異常,但是不管怎麼拋目前都是異常安全的控制代碼釋放結束,將弱引用置空標記已成功釋放

spStorage->second = nullptr; // weak_ptr賦值不丟擲假設所有失效控制代碼都成功釋放沒丟擲才會迴圈跑完來到此處,使用erase-remove手法清理掉陣列裡面的空弱引用(已釋放)

auto new_end = std::remove_if(g_vecHandleList.begin(), g_vecHandleList.

endstd::shared_ptr< Storage_t > spStorage)

對應weak_ptr的三種狀態:

失效spStorage->second.lock() == nullptr —— 資源已無人使用,可以清理

正常spStorage->second.lock() != nullptr —— 該資源還有人用,不能清理

這個函式檢索g_vecHandleList中所有已經失效的弱引用,並將它們清理出去。如果FreeHandle函式丟擲異常,上層捕獲異常並處理後仍然可以重試清理(成功清理的會被mark為空弱引用)。

原始靈感來自https://

更新:2019-12-31 CreateHandlePtr的return中的second應改為first

10樓:

析構函式不丟擲異常是因為析構函式丟擲異常則當是丟擲異常並處理異常時再丟擲異常會導致程式不正常中止。

一般在析構函式中try塊吃點異常而不進行處理。

在C 中 String Empty和 有什麼區別?

Ken.W 這類問題去stackoverflow問效果更好 榮浩 String.Empty是String類裡已經例項化好的靜態唯讀空字串,我們可以直接拿來用。而對於 系統需要去託管堆上例項化乙個空字串,效能略差一些吧 這種說法是錯誤的!Strings1 String Empty Strings2 C...

有什麼在數值上差異極小或者在描述程度中可以忽略不計但實際上差距極度遙遠的事物或者觀念

幻想鄉2 歷史上的大多數人。當然,也包括你我。世界大多數歷史都是王侯將相之歷史,普通人甚至連乙個記錄都不會有。但正是這些普通人構成的日常,組成了 歷史 的大部分內容。 起個名字吧 我們把小球看做乙個質點,忽略它的體積,質量。廣義相對論 去NM的。假設這個物體是乙個理想黑體,那麼它的輻射。光學儀器 我...

怎樣理解C 中「如果屬性有在堆區開闢的,一定要自己提供拷貝建構函式,防止淺拷貝帶來的問題」?

CuKing 對但不完全對。對是因為,比如實現乙個字串類,裡面有乙個指標指向字串所在地,析構是delete這個指標,那麼拷貝直接拷這個指標,之後會析構兩次就delete兩次就掛了。不對是因為,比如我可能雖然存了乙個指標,但是另有地方統一管理這個資源,那當然也沒必要提供。其實這種一刀切的話基本沒幾句是...