詳解C++中對結構函數和賦值運算符的復制和挪動操作。本站提示廣大學習愛好者:(詳解C++中對結構函數和賦值運算符的復制和挪動操作)文章只能為提供參考,不一定能成為您想要的結果。以下是詳解C++中對結構函數和賦值運算符的復制和挪動操作正文
復制結構函數和復制賦值運算符
從 C++ 11 中開端,該說話支撐兩品種型的分派:復制賦值和挪動賦值。 在本文中,“賦值”意味著復制賦值,除非有其他顯式聲明。 賦值操作和初始化操作都邑招致對象被復制。
賦值:在將一個對象的值賦給另外一個對象時,第一個對象將復制到第二個對象中。 是以,
Point a, b; ... a = b;
招致 b 的值被復制到 a 中。
初始化:在以下情形下將停止初始化:聲明新對象、參數經由過程值傳遞給函數或值經由過程值從函數前往。
您可認為類類型的對象界說“復制”的語義。 例如,斟酌此代碼:
TextFile a, b; a.Open( "FILE1.DAT" ); b.Open( "FILE2.DAT" ); b = a;
後面的代碼能夠表現“將 FILE1.DAT 的內容復制到 FILE2.DAT”,也能夠表現“疏忽 FILE2.DAT 並使 b 成為 FILE1.DAT 的另外一個句柄”。 您必需將恰當的復制語義附加到每一個類,以下所示。
經由過程將賦值運算符 operator= 與對類類型的援用一路用作前往類型和 const 援用所傳遞的參數(例如,ClassName& operator=(const ClassName& x);)。
經由過程經由過程復制結構函數。 有關復制結構函數的具體信息,請參閱聲明結構函數的規矩。
假如不聲明復制結構函數,編譯器將為你生成 member-wise 復制結構函數。 假如不聲明復制賦值運算符,編譯器將為你生成 member-wise 復制賦值運算符。 聲明復制結構函數不會撤消編譯器生成的復制賦值運算符,反之亦然。 假如完成上述個中一項,建議您還完成另外一項以使代碼的寄義變得明白。
逐一成員賦值和初始化 中更具體地引見了逐一成員賦值。
復制結構函數采取了 class-name& 類型的參數,個中 class-name 是為其界說結構函數的類的稱號。 例如:
// spec1_copying_class_objects.cpp class Window { public: Window( const Window& ); // Declare copy constructor. // ... }; int main() { }
解釋:
盡量創立該類型的復制結構函數的參數 const class-name&。 這可避免復制結構函數不測更改從中復制它的對象。 它還支撐從 const 對象停止復制。
編譯器生成的結構函數
編譯器生成的復制結構函數(如用戶界說的復制結構函數)具有單個參數類型“對 class-name 的援用”。 當一切基類和成員類都具有聲明為采取 const class-name& 類型的單個參數的復制結構函數時,將激發異常。 在這類情形下,編譯器生成的復制結構函數的參數也是 const。
當復制結構函數的參數類型不是 const 時,經由過程復制 const 對象停止初始化將發生毛病。 反之則否則:假如參數是 const,您可以經由過程復制不是 const 的對象停止初始化。
編譯器生成的賦值運算符遵守關於 const 的雷同形式。 除非一切基類和成員類中的賦值運算符都采取 const class-name& 類型的參數,不然它們將采取 class-name& 類型的單個參數。 在這類情形下,類的生成的賦值運算符采取 const 參數。
解釋:
當虛擬基類由復制結構函數(編譯器生成或用戶界說的)初始化時,將只初始化這些基類一次:在結構它們時。
寄義相似於復制結構函數的寄義。 當參數類型不是 const 時,從 const 對象賦值將發生毛病。 反之則否則:假如將 const 值賦給不是 const 的值,則賦值能勝利。
挪動結構函數和挪動賦值運算符
上面的示例基於用於治理內存緩沖區的 C++ 類 MemoryBlock。
// MemoryBlock.h #pragma once #include <iostream> #include <algorithm> class MemoryBlock { public: // Simple constructor that initializes the resource. explicit MemoryBlock(size_t length) : _length(length) , _data(new int[length]) { std::cout << "In MemoryBlock(size_t). length = " << _length << "." << std::endl; } // Destructor. ~MemoryBlock() { std::cout << "In ~MemoryBlock(). length = " << _length << "."; if (_data != nullptr) { std::cout << " Deleting resource."; // Delete the resource. delete[] _data; } std::cout << std::endl; } // Copy constructor. MemoryBlock(const MemoryBlock& other) : _length(other._length) , _data(new int[other._length]) { std::cout << "In MemoryBlock(const MemoryBlock&). length = " << other._length << ". Copying resource." << std::endl; std::copy(other._data, other._data + _length, _data); } // Copy assignment operator. MemoryBlock& operator=(const MemoryBlock& other) { std::cout << "In operator=(const MemoryBlock&). length = " << other._length << ". Copying resource." << std::endl; if (this != &other) { // Free the existing resource. delete[] _data; _length = other._length; _data = new int[_length]; std::copy(other._data, other._data + _length, _data); } return *this; } // Retrieves the length of the data resource. size_t Length() const { return _length; } private: size_t _length; // The length of the resource. int* _data; // The resource. };
以下進程引見若何為示例 C++ 類編寫挪動結構函數和挪動賦值運算符。
為 C++ 創立挪動結構函數
界說一個空的結構函數辦法,該辦法采取一個對類類型的右值援用作為參數,如以下示例所示:
MemoryBlock(MemoryBlock&& other) : _data(nullptr) , _length(0) { }
在挪動結構函數中,將源對象中的類數據成員添加到要結構的對象:
_data = other._data; _length = other._length;
將源對象的數據成員分派給默許值。 如許可以避免析構函數屢次釋放資本(如內存):
other._data = nullptr; other._length = 0;
為 C++ 類創立挪動賦值運算符
界說一個空的賦值運算符,該運算符采取一個對類類型的右值援用作為參數並前往一個對類類型的援用,如以下示例所示:
MemoryBlock& operator=(MemoryBlock&& other) { }
在挪動賦值運算符中,假如測驗考試將對象賦給本身,則添加不履行運算的前提語句。
if (this != &other) { }
在前提語句中,從要將其賦值的對象中釋放一切資本(如內存)。
以下示例從要將其賦值的對象中釋放 _data 成員:
// Free the existing resource. delete[] _data;
履行第一個進程中的步調 2 和步調 3 以將數據成員從源對象轉移到要結構的對象:
// Copy the data pointer and its length from the // source object. _data = other._data; _length = other._length; // Release the data pointer from the source object so that // the destructor does not free the memory multiple times. other._data = nullptr; other._length = 0;
前往對以後對象的援用,如以下示例所示:
return *this;
以下示例顯示了 MemoryBlock 類的完全挪動結構函數和挪動賦值運算符:
// Move constructor. MemoryBlock(MemoryBlock&& other) : _data(nullptr) , _length(0) { std::cout << "In MemoryBlock(MemoryBlock&&). length = " << other._length << ". Moving resource." << std::endl; // Copy the data pointer and its length from the // source object. _data = other._data; _length = other._length; // Release the data pointer from the source object so that // the destructor does not free the memory multiple times. other._data = nullptr; other._length = 0; } // Move assignment operator. MemoryBlock& operator=(MemoryBlock&& other) { std::cout << "In operator=(MemoryBlock&&). length = " << other._length << "." << std::endl; if (this != &other) { // Free the existing resource. delete[] _data; // Copy the data pointer and its length from the // source object. _data = other._data; _length = other._length; // Release the data pointer from the source object so that // the destructor does not free the memory multiple times. other._data = nullptr; other._length = 0; } return *this; }
以下示例演示挪動語義若何能進步運用法式的機能。此示例將兩個元素添加到一個矢量對象,然後在兩個現有元素之間拔出一個新元素。在 Visual C++ 2010 中,vector 類應用挪動語義,經由過程挪動矢量元素(而非復制它們)來高效地履行拔出操作。
// rvalue-references-move-semantics.cpp // compile with: /EHsc #include "MemoryBlock.h" #include <vector> using namespace std; int main() { // Create a vector object and add a few elements to it. vector<MemoryBlock> v; v.push_back(MemoryBlock(25)); v.push_back(MemoryBlock(75)); // Insert a new element into the second position of the vector. v.insert(v.begin() + 1, MemoryBlock(50)); }
該示例發生上面的輸入:
In MemoryBlock(size_t). length = 25. In MemoryBlock(MemoryBlock&&). length = 25. Moving resource. In ~MemoryBlock(). length = 0. In MemoryBlock(size_t). length = 75. In MemoryBlock(MemoryBlock&&). length = 25. Moving resource. In ~MemoryBlock(). length = 0. In MemoryBlock(MemoryBlock&&). length = 75. Moving resource. In ~MemoryBlock(). length = 0. In MemoryBlock(size_t). length = 50. In MemoryBlock(MemoryBlock&&). length = 50. Moving resource. In MemoryBlock(MemoryBlock&&). length = 50. Moving resource. In operator=(MemoryBlock&&). length = 75. In operator=(MemoryBlock&&). length = 50. In ~MemoryBlock(). length = 0. In ~MemoryBlock(). length = 0. In ~MemoryBlock(). length = 25. Deleting resource. In ~MemoryBlock(). length = 50. Deleting resource. In ~MemoryBlock(). length = 75. Deleting resource.
應用挪動語義的此示例版本比不應用挪動語義的版本更高效,由於前者履行的復制、內存分派和內存釋放操作更少。
靠得住編程
若要避免資本洩露,請一直釋放挪動賦值運算符中的資本(如內存、文件句柄和套接字)。
若要避免弗成恢復的資本破壞,請准確處置挪動賦值運算符中的自我賦值。
假如為您的類同時供給了挪動結構函數和挪動賦值運算符,則可以編寫挪動結構函數來挪用挪動賦值運算符,從而清除冗余代碼。以下示例顯示了挪用挪動賦值運算符的挪動結構函數的修正後的版本:
// Move constructor. MemoryBlock(MemoryBlock&& other) : _data(nullptr) , _length(0) { *this = std::move(other); }