我對更新一個舊的個人專案到現代C 感興趣。我很欣賞RAII對清理作業的簡化:不需要制作一個new物件并在函式的每個回傳點之前記得delete它,只需要make_unique,它就會被適當地銷毀。但是在比較生成的程式集時,我有一個挑剔的地方。
假設有一個類方法用一個新的值替換了它的一個unique_ptr成員:
// std::unique_ptr<int> MyClass::m_foo;/span>
void MyClass::refresh_foo(int x){
m_foo = std::make_unique<int> (x * 3 5);
}
這將創建一個新的 int,將其分配給m_foo,然后洗掉 m_foo的舊值。但這與舊的行為不太一樣,它可以洗掉舊的值,然后創建一個新的,并將其分配給m_foo:
// int *MyClass::m_foo;
void MyClass::refresh_foo(int x){
delete m_foo;
m_foo = new int(x * 3 5) 。
}
從Compiler Explorer來看,gcc、clang和MSVC對舊方法生成的代碼都比新方法少。我理解為什么會出現這種情況,因為這是所有運算式的評估順序;p = q;必須先構造q。當然,m_foo在delete和new行之間有一個無效的值。但我是否錯過了一種方法,讓unique_ptr在一個運算式中銷毀它的舊值,然后創建一個新的值?一個"replace_unique"?在處理大型物件時,這似乎很有幫助,這樣兩個物件就不會無謂地共存了。
編輯:
<編輯:明確地說,我在這里只是用int作為一個微不足道的例子。我的實際用例是使用我自己的類或來自一個庫的類。而m_foo.reset(new int(x * 3 5));與舊的做事方式相比,自然也有同樣的問題,因為它也必須在賦值和delet前構建new int。
uj5u.com熱心網友回復:
你可以使用unique_ptr::reset()來在你想要的時候確定性地銷毀當前持有的物件。用一個nullptr指標更新unique_ptr,然后再用一個新的物件指標來更新它,例如:
// std::unique_ptr<int> MyClass::m_foo;/span>
void MyClass::refresh_foo(int x){
m_foo.reset(); // <- int在這里被銷毀。
m_foo = std::make_unique<int>(x * 3 5) 。
}
uj5u.com熱心網友回復:
我接受了在m_foo = std::make_unique<int>(...)之前呼叫m_foo.reset()的答案,因為它是解決上述問題的最簡單方法:在構建新值之前破壞原值。然而,這確實導致了一個額外的delete呼叫,這不應該是必要的,而且不如使用原始指標的 "前現代 "方式更理想。
這種方法,至少在Compiler Explorer與-Ofast中,避免了第二個delete呼叫,并且與老方法(在分配其新構造的值之前,明確地將m_foo設定為nullptr)相比,只有一個額外的mov指令:
// std::unique_ptr<int> MyClass::m_foo;/span>
void refresh_smart(intx){
m_foo = nullptr;
auto new_foo = std::make_unique<int> (x * 3 5) 。
m_foo.swap(new_foo)。
new_foo.release(); //沒有記憶體泄漏,因為它是空的。
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/333968.html
標籤:
