C++ 從基礎到精通 — 第 14 講
複製和移動有什麼區別?
該部落格上撰寫的文章是出於教學目的而創建的。 另外,文章中的資訊可能不正確,請謹慎。
當將大物件插入向量或從函數傳回物件時,似乎會發生大量複製。 在 C++ 中,複製(按原樣複製值)和移動(接管不再需要的物件的資源)之間存在差異。
複製
複製會建立一個與原始物件具有相同值的新物件。
class Texture
{
public:
explicit Texture(std::vector<unsigned char> pixels)
: pixels(std::move(pixels))
{
}
private:
std::vector<unsigned char> pixels;
};
Texture original(LoadPixels());
Texture copied = original;
copied建立向量時,向量內部的資料也會被複製。 由於兩個物件彼此獨立,因此修改複製的物件不會更改原始物件。
小值的複製成本很小,但具有大緩衝區或許多字串的物件可能會很昂貴。
移動
如果不再使用原始對象,則可以將內部資源傳遞給新對象。
Texture loaded(LoadPixels());
Texture equipped = std::move(loaded);
std::move不直接傳輸資料。 我們更改物件以將其視為可移動值,實際工作由移動建構函數或移動賦值運算子完成。
搬家了loaded仍然可以被銷毀並可以分配新的值。 但是,您不應期望內部狀態與以前相同。
loaded = Texture(LoadPixels()); //可以替換新值。
Rule of Zero
如果直接用new和delete管理內存,就必須一起考慮複製建構子、複製賦值、移動建構子、移動賦值、析構函式。 即使其中一個實作不正確,也可能會發生雙重釋放或記憶體洩漏。
class CharacterData
{
public:
std::string name;
std::vector<int> learnedSkills;
std::unique_ptr<Weapon> weapon;
};
使用標準容器和智慧指標作為成員,每種類型都會管理自己的資源。 這種不直接寫特殊成員函數的方法稱為零規則。
unique_ptr 無法複製;它只能被移動。 此限制可防止在編譯階段意外複製唯一所有權。
如何傳遞值
只讀的大對象const T&當函數取得所有權時,通常使用將其作為值接收然後移動它的方法。
class Party
{
public:
void Add(CharacterData character)
{
members.push_back(std::move(character));
}
private:
std::vector<CharacterData> members;
};
Party party;
CharacterData warrior;
party.Add(std::move(warrior));
CharacterData 有 unique_ptr 作為成員,因此不能複製,只能移動。 新增現有物件時,必須使用 std::move 傳遞所有權或傳遞臨時物件。
到處添加 std::move 並不是最佳選擇。
您必須先決定它是一個需要複製的值還是一個您可以傳遞所有權的值。 移動並不是一種快速複製的語法,而是一種在轉移資源後不依賴原始狀態的表達。