[英]Why do we need to set rvalue reference to null in move constructor?
//code from https://skillsmatter.com/skillscasts/2188-move-semanticsperfect-forwarding-and-rvalue-references
class Widget {
public:
Widget(Widget&& rhs)
: pds(rhs.pds) // take source’s value
{
rhs.pds = nullptr; // why??
}
private:
struct DataStructure;
DataStructure *pds;
};
我無法理解將rhd.pds
設置為nullptr
的原因。
如果我們刪除這一行會發生什么: rhs.pds = nullptr;
該類的一些細節已被刪除。 特別是,構造函數動態分配DataStructure
對象,析構函數釋放它。 如果在移動過程中,您只是將指針從一個Widget
復制到另一個Widget
,則兩個Widget
都將具有指向同一個分配的DataStructure
對象的指針。 然后,當這些對象被銷毀時,它們都會嘗試delete
它。 這將給出未定義的行為。 為了避免這種情況,正在移動的Widget
將其內部指針設置為nullptr
。
這是實現移動構造函數時的標准模式。 您希望將一些動態分配的對象的所有權從一個對象轉移到另一個對象,因此您需要確保原始對象不再擁有這些分配的對象。
從圖表上看,您從這種情況開始,希望將DataStructure
所有權從一個Widget
轉移到另一個Widget
:
┌────────┐ ┌────────┐
│ Widget │ │ Widget │
└───╂────┘ └────────┘
┃
▼
┌───────────────┐
│ DataStructure │
└───────────────┘
如果你只是復制了指針,你會有:
┌────────┐ ┌────────┐
│ Widget │ │ Widget │
└───╂────┘ └───╂────┘
┗━━━━━━━━┳━━━━━━━┛
▼
┌───────────────┐
│ DataStructure │
└───────────────┘
如果您隨后將原始Widget
指針設置為nullptr
,您將:
┌────────┐ ┌────────┐
│ Widget │ │ Widget │
└────────┘ └───╂────┘
┃
▼
┌───────────────┐
│ DataStructure │
└───────────────┘
所有權已成功轉移,並且當兩個Widget
都可以被銷毀而不會導致未定義的行為時。
DataStructure
對象可能由Widget
“擁有”,重置指針可以防止它在Widget
被銷毀時被意外刪除。
或者,在移動對象時將對象重置為“空”或“默認”狀態是慣例,並且重置指針是遵循約定的無害方式。
class Widget {
public:
Widget(Widget&& rhs)
: pds(rhs.pds) // take source’s value
{
rhs.pds = nullptr; // why??
}
~Widget() {delete pds}; // <== added this line
private:
struct DataStructure;
DataStructure *pds;
};
我在上面的類中添加了一個析構函數。
Widget make_widget() {
Widget a;
// Do some stuff with it
return std::move(a);
}
int main {
Widget b = make_widget;
return 0;
}
為了說明如果刪除 nullptr 分配會發生什么,請檢查上述方法。 小部件 a 將在輔助函數中創建並分配給小部件 b。
由於小部件 a 超出了其調用的析構函數的作用域,它會釋放內存,而小部件 b 則指向無效的內存地址。
如果將 nullptr 分配給 rhs,也會調用析構函數,但由於 delete nullptr 什么都不做,一切都很好:)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.