簡體   English   中英

std :: unique_ptr的強異常保證 <T> ::重啟

[英]Strong exception guarantee for std::unique_ptr<T>::reset

假設我有一個功能reset

template<typename T, typename... Args>
void reset(unique_ptr<T>& p, Args&&... args)
{
    // implementation (1)
    p.reset(new T(forward<Args>(args)...));

    // implementation (2)
    p = make_unique<T>(forward<Args>(args)...);
}

我糾正的是:

  1. 對於實現(1),如果在銷毀p的原始指針期間拋出異常,則new內存將被泄露;

  2. 對於實施(2),沒有任何東西可以泄露;

  3. 所以我們應該更喜歡(2)到(1)。

正如Jonathan Wakely 指出的那樣 ,這一點沒有實際意義,因為如果析構函數拋出,則unique_ptr::reset行為是未定義的。

如果析構函數拋出,則兩個版本都具有UB,因此這不是優先於其他版本的理由。

[unique.ptr.single.modifiers] (標准草案)

3要求:表達式get_deleter()(get())應該格式正確,應具有明確定義的行為,並且不應拋出異常


即使行為定義明確......

(1)不會泄漏。 unique_ptr在破壞舊參數之前獲取參數的所有權。

4效果:將p分配給存儲的指針,然后如果存儲指針old_p的舊值不等於nullptr ,則調用get_deleter()(old_p) [注意:這些操作的順序很重要,因為調用get_deleter()可能會破壞*this - 結束說明]


偏好一個的原因而不是另一個

  • (1)只需要C ++ 11,(2)需要C ++ 14或者你自己的make_unique樣板。
  • (2)沒有明確調用new ,因此使用舊的經驗法則更容易推理內存整潔:“每個新刪除一個”。
template<typename T, typename... Args>
void reset(unique_ptr<T>& p, Args&&... args)
{
  auto tmp = std::make_unique<T>(std::forward<Args>(args)...);
  std::swap( tmp, p );
}

正如另一個答案所指出的, reset要求析構函數不會拋出。

然而,上述內容並沒有提出這樣的要求。 swap不應該拋出。 如果make_unique的ctor拋出, make_unique發生明顯的事情。 tmp的ctor不會拋出。

如果tmp的dtor拋出,它已經包含p包含的內容,並且p已經包含新數據。 所以事情處於可預測的狀態。

這不是強大的異常保證,因為如果發生拋出(在破壞p的內容期間),事物不會回滾到原始狀態。 我認為沒有合理的方法來提供這種保證:也許如果clone T不是嗎? 但即便如此,如果p拋出,任何臨時對象也會拋出,所以事情並不真實。

所有~unique_ptrunique_ptr::operator=unique_ptr::reset都是noexcept 因此,從unique_ptr擁有的對象的析構函數拋出異常將始終導致std::terminate 這使得內存泄漏在兩種情況下都無關緊要。

這里解釋了為什么make_shared / make_uniqueunique_ptr(new ...) / shared_ptr(new ...)更可取。 這就是為什么我們應該更喜歡(2)到(1)

不要忘記:您無法使用make_shared / make_unique指定自定義刪除make_unique

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM