【1】左值引用和右值引用
左值引用(一般所謂的引用形式)使標識符關聯到左值。
何為左值?左值是一個表示數據的表達式(如變量名、解除引用的指針)。
最初,左值可出現在賦值語句的左邊,但修飾符const的出現使得可以聲明這樣的標識符(即不能給它賦值,但可獲取其地址)。
1 int n = 10; 2 int * pt = new int; 3 int & rn = n; // 變量名 4 int & rt = *pt; // 解除引用的指針 5 const int b = 20; // 不可以給b賦值,但可獲取其地址 6 // b = 21; // error 7 const int * pb = &b; // ok 8 const int & rb = b; // 不可以給rb賦值,但可獲取其地址 9 // rb = n; // error 10 const int * prb = &rb; // ok
總而言之,判斷左值的唯一條件是程序可獲取其地址(即可對其應用地址運算符)。
右值引用使標識符關聯到右值。右值引用是使用&&表示的。右值即可出現在賦值表達式右邊,但不能對其應用地址運算符的值(與左值相反)。
右值包括字面常量(C-風格字符串除外,因為它表示地址),比如x+y等表達式以及返回值的函數(條件是該函數返回的不是引用)。
舉例如下:
1 int x = 10; 2 int y = 11; 3 int && r1 = 12; // 常量 4 int && r2 = x + y; // 表達式 5 double && r3 = std::sqrt(2.0); // 函數返回值
通過示例可以發現:將右值關聯到右值引用導致該右值被存儲到特定的位置,並且可通過標識符獲取該位置的地址。也就是說,雖然不可將地址運算符&應用於12,但可將其用於r1。將數據與特定的地址關聯,使得可通過右值引用來訪問該數據信息。
下面演示及驗證右值引用的作用:
1 #include <iostream> 2 using namespace std; 3 4 double f(double tf) { return tf/20; }; 5 6 void main() 7 { 8 double tc = 10.5; 9 double && rd1 = 100.01; 10 double && rd2 = 1.8 * tc; 11 double && rd3 = f(rd2); 12 cout << "tc Value And Address: " << tc << " " << &tc << endl; 13 cout << "rd1 Value And Address: " << rd1 << " " << &rd1 << endl; 14 cout << "rd2 Value And Address: " << rd2 << " " << &rd2 << endl; 15 cout << "rd3 Value And Address: " << rd3 << " " << &rd3 << endl; 16 cin.get(); 17 } 18 // run out 19 /* 20 tc Value And Address: 10.5 003FFAE4 21 rd1 Value And Address: 100.01 003FFAC8 22 rd2 Value And Address: 18.9 003FFAAC 23 rd3 Value And Address: 0.945 003FFA90 24 */
為啥需要右值引用呢?引入右值引用的主要目的之一是實現移動語義。
【2】移動語義之移動構造函數
2.1 為何需要移動語義?
請看如下示例代碼(相信你我已經寫過很多類似的),只有復制構造函數情況下:
1 // 例1:只有復制構造函數 2 #include <iostream> 3 using namespace std; 4 5 // interface 6 class Useless 7 { 8 private: 9 int n; // number of elements 10 char * pc; // pointer to data 11 static int ct; // number of objects 12 void ShowObject() const; 13 14 public: 15 Useless(); 16 explicit Useless(int k); 17 Useless(int k, char ch); 18 Useless(const Useless & f); // regular copy constructor 19 ~Useless(); 20 Useless operator+(const Useless & f)const; 21 void ShowData() const; 22 }; 23 24 // implementation 25 int Useless::ct = 0; 26 27 Useless::Useless() 28 { 29 ++ct; 30 n = 0; 31 pc = nullptr; 32 cout << "default constructor called; number of objects: " << ct << endl; 33 ShowObject(); 34 } 35 36 Useless::Useless(int k) : n(k) 37 { 38 ++ct; 39 cout << "Useless(int k) constructor called; number of objects: " << ct << endl; 40 pc = new char[n]; 41 ShowObject(); 42 } 43 44 Useless::Useless(int k, char ch) : n(k) 45 { 46 ++ct; 47 cout << "Useless(int k, char ch) constructor called; number of objects: " << ct << endl; 48 pc = new char[n]; 49 for (int i = 0; i < n; i++) 50 pc[i] = ch; 51 ShowObject(); 52 } 53 54 Useless::Useless(const Useless & f) : n(f.n) 55 { 56 ++ct; 57 cout << "copy constructor const called; number of objects: " << ct << endl; 58 pc = new char[n]; 59 for (int i = 0; i < n; i++) 60 pc[i] = f.pc[i]; 61 ShowObject(); 62 } 63 64 Useless::~Useless() 65 { 66 cout << "destructor called; "; 67 cout << "deleted object:\n"; 68 ShowObject(); 69 delete [] pc; 70 cout << "objects left: " << --ct << endl << endl; 71 } 72 73 Useless Useless::operator+(const Useless & f)const 74 { 75 cout << "Entering operator+()\n"; 76 Useless temp = Useless(n + f.n); 77 for (int i = 0; i < n; i++) 78 temp.pc[i] = pc[i]; 79 for (int i = n; i < temp.n; i++) 80 temp.pc[i] = f.pc[i - n]; 81 cout << "temp object:\n"; 82 cout << "Leaving operator+()\n"; 83 return temp; 84 } 85 86 void Useless::ShowObject() const 87 { 88 cout << "Number of elements: " << n; 89 cout << " Data address: " << (void *) pc << endl << endl; 90 } 91 92 void Useless::ShowData() const 93 { 94 if (0 == n) 95 { 96 cout << "(object empty)"; 97 } 98 else 99 { 100 for (int i = 0; i < n; i++) 101 cout << pc[i]; 102 } 103 cout << endl; 104 } 105 106 // application 107 int main() 108 { 109 { 110 Useless one(10, 'x'); 111 Useless two = one; // calls copy constructor 112 Useless three(20, 'o'); 113 Useless four(one + three); // calls operator+(), copy constructor 114 cout << "object one: "; 115 one.ShowData(); 116 cout << "object two: "; 117 two.ShowData(); 118 cout << "object three: "; 119 three.ShowData(); 120 cout << "object four: "; 121 four.ShowData(); 122 } 123 cin.get(); 124 } 125 126 // out 127 /* 128 Useless(int k, char ch) constructor called; number of objects: 1 129 Number of elements: 10 Data address: 004A4910 130 131 copy constructor const called; number of objects: 2 132 Number of elements: 10 Data address: 004A4958 133 134 Useless(int k, char ch) constructor called; number of objects: 3 135 Number of elements: 20 Data address: 004A49A0 136 137 Entering operator+() 138 Useless(int k) constructor called; number of objects: 4 139 Number of elements: 30 Data address: 004A49F0 140 141 temp object: 142 Leaving operator+() 143 copy constructor const called; number of objects: 5 144 Number of elements: 30 Data address: 004A4C50 145 146 destructor called; deleted object: 147 Number of elements: 30 Data address: 004A49F0 148 149 objects left: 4 150 151 object one: xxxxxxxxxx 152 object two: xxxxxxxxxx 153 object three: oooooooooooooooooooo 154 object four: xxxxxxxxxxoooooooooooooooooooo 155 destructor called; deleted object: 156 Number of elements: 30 Data address: 004A4C50 157 158 objects left: 3 159 160 destructor called; deleted object: 161 Number of elements: 20 Data address: 004A49A0 162 163 objects left: 2 164 165 destructor called; deleted object: 166 Number of elements: 10 Data address: 004A4958 167 168 objects left: 1 169 170 destructor called; deleted object: 171 Number of elements: 10 Data address: 004A4910 172 173 objects left: 0 174 175 */
運行結果分析如下:

從運算符+重載開始琢磨,如下:
1 Useless Useless::operator+(const Useless & f)const 2 { 3 cout << "Entering operator+()\n"; 4 Useless temp = Useless(n + f.n); 5 for (int i = 0; i < n; i++) 6 temp.pc[i] = pc[i]; 7 for (int i = n; i < temp.n; i++) 8 temp.pc[i] = f.pc[i - n]; 9 cout << "temp object:\n"; 10 cout << "Leaving operator+()\n"; 11 return temp; 12 }
運算符+重載實現中,創建了一個局部對象temp(注意與臨時對象的區別),最終函數結束時返回該對象。
由於函數返回值類型,所以這里只能調用復制構造函數來創建對象four。如下:
1 Useless::Useless(const Useless & f) : n(f.n) 2 { 3 ++ct; 4 cout << "copy constructor const called; number of objects: " << ct << endl; 5 pc = new char[n]; 6 for (int i = 0; i < n; i++) 7 pc[i] = f.pc[i]; 8 ShowObject(); 9 }
因此復制構造函數的形參f直接指向該局部對象(即實參),通過另開辟空間、深拷貝該局部對象從而完成創建對象four的任務。
然后,析構掉該局部對象。關鍵這里白白析構掉這個局部對象申請的內存空間的確有點太奢侈了,尤其從時間方面權衡整個過程。
如果,有一種構造函數可讓對象four的成員變量pc指針直接指向該局部對象已開辟的內存空間,而不需用再像復制構造函數那樣又另開辟空間來創建對象four。
那么,這個過程將節省多少時間呢?這里要點是做了大量的無用功。即就是說,將臨時對象的所有權直接交給對象four,不是更完美嗎?
2.2 移動語義是什么?
請看如下示例,增加移動構造函數:
1 // 例2:復制構造函數 與 移動構造函數 2 // useless.cpp -- an otherwise useless class with move semantics 3 #include <iostream> 4 using namespace std; 5 6 // interface 7 class Useless 8 { 9 private: 10 int n; // number of elements 11 char * pc; // pointer to data 12 static int ct; // number of objects 13 void ShowObject() const; 14 15 public: 16 Useless(); 17 explicit Useless(int k); 18 Useless(int k, char ch); 19 Useless(const Useless & f); // regular copy constructor 20 Useless(Useless && f); // move constructor 21 Useless & operator=(const Useless & f); // copy assignment 22 Useless & operator=(Useless && f); // move assignment 23 ~Useless(); 24 Useless operator+(const Useless & f) const; 25 void ShowData() const; 26 }; 27 28 // implementation 29 int Useless::ct = 0; 30 31 Useless::Useless() 32 { 33 ++ct; 34 n = 0; 35 pc = nullptr; 36 cout << "default constructor called; number of objects: " << ct << endl; 37 ShowObject(); 38 } 39 40 Useless::Useless(int k) : n(k) 41 { 42 ++ct; 43 cout << "Useless(int k) constructor called; number of objects: " << ct << endl; 44 pc = new char[n]; 45 ShowObject(); 46 } 47 48 Useless::Useless(int k, char ch) : n(k) 49 { 50 ++ct; 51 cout << "Useless(int k, char ch) constructor called; number of objects: " << ct << endl; 52 pc = new char[n]; 53 for (int i = 0; i < n; i++) 54 pc[i] = ch; 55 ShowObject(); 56 } 57 58 Useless::Useless(const Useless & f) : n(f.n) 59 { 60 ++ct; 61 cout << "copy constructor const called; number of objects: " << ct << endl; 62 pc = new char[n]; 63 for (int i = 0; i < n; i++) 64 pc[i] = f.pc[i]; 65 ShowObject(); 66 } 67 68 Useless::Useless(Useless && f) : n(f.n) 69 { 70 ++ct; 71 cout << "move constructor called; number of objects: " << ct << endl; 72 pc = f.pc; // steal address 73 f.pc = nullptr; // give old object nothing in return 74 f.n = 0; 75 ShowObject(); 76 } 77 78 Useless & Useless::operator=(const Useless & f) 79 { 80 if (this == &f) 81 return *this; 82 delete []pc; 83 n = f.n; 84 pc = new char[n]; 85 for (int i = 0; i < n; ++i) 86 pc[i] = f.pc[i]; 87 return *this; 88 } 89 90 Useless & Useless::operator=(Useless && f) 91 { 92 if (this == &f) 93 return *this; 94 delete []pc; 95 n = f.n; 96 pc = f.pc; 97 f.n = 0; 98 f.pc = nullptr; 99 return *this; 100 } 101 102 Useless::~Useless() 103 { 104 cout << "destructor called; "; 105 cout << "deleted object:\n"; 106 ShowObject(); 107 delete [] pc; 108 cout << "objects left: " << --ct << endl << endl; 109 } 110 111 Useless Useless::operator+(const Useless & f)const 112 { 113 cout << "Entering operator+()\n"; 114 Useless temp = Useless(n + f.n); 115 for (int i = 0; i < n; i++) 116 temp.pc[i] = pc[i]; 117 for (int i = n; i < temp.n; i++) 118 temp.pc[i] = f.pc[i - n]; 119 cout << "temp object:\n"; 120 cout << "Leaving operator+()\n"; 121 return temp; 122 } 123 124 void Useless::ShowObject() const 125 { 126 cout << "Number of elements: " << n; 127 cout << " Data address: " << (void *) pc << endl << endl; 128 } 129 130 void Useless::ShowData() const 131 { 132 if (0 == n) 133 { 134 cout << "(object empty)"; 135 } 136 else 137 { 138 for (int i = 0; i < n; i++) 139 cout << pc[i]; 140 } 141 cout << endl; 142 } 143 144 // application 145 int main() 146 { 147 { 148 Useless one(10, 'x'); 149 Useless two = one; // calls copy constructor 150 Useless three(20, 'o'); 151 Useless four(one + three); // calls operator+(), move constructor 152 cout << "object one: "; 153 one.ShowData(); 154 cout << "object two: "; 155 two.ShowData(); 156 cout << "object three: "; 157 three.ShowData(); 158 cout << "object four: "; 159 four.ShowData(); 160 } 161 cin.get(); 162 } 163 164 // out 165 /* 166 Useless(int k, char ch) constructor called; number of objects: 1 167 Number of elements: 10 Data address: 00224910 168 169 copy constructor const called; number of objects: 2 170 Number of elements: 10 Data address: 00224958 171 172 Useless(int k, char ch) constructor called; number of objects: 3 173 Number of elements: 20 Data address: 002249A0 174 175 Entering operator+() 176 Useless(int k) constructor called; number of objects: 4 177 Number of elements: 30 Data address: 002249F0 178 179 temp object: 180 Leaving operator+() 181 move constructor called; number of objects: 5 182 Number of elements: 30 Data address: 002249F0 183 184 destructor called; deleted object: 185 Number of elements: 0 Data address: 00000000 186 187 objects left: 4 188 189 object one: xxxxxxxxxx 190 object two: xxxxxxxxxx 191 object three: oooooooooooooooooooo 192 object four: xxxxxxxxxxoooooooooooooooooooo 193 destructor called; deleted object: 194 Number of elements: 30 Data address: 002249F0 195 196 objects left: 3 197 198 destructor called; deleted object: 199 Number of elements: 20 Data address: 002249A0 200 201 objects left: 2 202 203 destructor called; deleted object: 204 Number of elements: 10 Data address: 00224958 205 206 objects left: 1 207 208 destructor called; deleted object: 209 Number of elements: 10 Data address: 00224910 210 211 objects left: 0 212 213 */
運行結果分析如下:

注意:上面分析運行結果圖中,把temp稱為局部變量,這次運行結果分析圖中,把temp稱為了局部對象。
實質上,變量和對象是一回事,只不過人們習慣上把用內置類型定義的東東稱為變量,而把自定義類型定義的東東稱為對象(與類相匹配)。
通過實踐,我們發現:移動構造函數可以完美實現夙願。
從運行結果看到內存地址002249F0出現了三次:首次出現在構建局部對象時,二次出現在調用移動構造函數創建對象four時,第三次出現在析構對象four時。
很顯然,對象four的創建通過調用移動構造函數而沒有再另申請內存空間,僅僅只是改變了局部對象實質內容的所有權而已。
而觀察局部對象析構時的打印信息:其指針成員值為空。目的為了防止對同一塊內存多次delete引起程序的致命性錯誤。因為對空指針多次delete沒有任何意義。
如何理解程序中就多次delete同一塊內存了呢?
第一次,析構局部對象temp,其實釋放的正是內存002249F0,只不過在析構之前移動構造函數將其已移動完成並置為空。
第二次,析構對象four,釋放的也是內存002249F0。(還沒有看懂的話,可對比上面只有復制構造函數的程序運行結果再琢磨琢磨其中的套路。)
總結:這個過程類似於在計算機中移動文件的情形:實際文件還留在原來的地方,只不過僅僅修改記錄而已,這種方法被稱為移動語義。
實質上,移動語義即不移動原始數據,僅僅修改了記錄(指針指向)而已。
2.3 C++11如何支持移動語義?
要實現移動語義,需要采取某種方式,讓編譯器知道什么時候需要復制,什么時候不需要,而這正是右值引用發揮作用的地方。
可定義兩個構造函數,其中一個是常規復制構造函數,它使用const左值引用作為參數,這個引用關聯到左值實參;
另一個是移動構造函數,它使用右值引用作為參數,該引用關聯到右值實參。復制構造函數可執行深復制,而移動構造函數只調整記錄。
在將所有權轉移給新對象的過程中,移動構造函數可能修改其實參,這意味着右值引用參數不應用const修飾。
雖然使用右值引用可支持移動語義,但這並不會神奇的發生。要讓移動語義發生,需要兩個步驟:
1. 右值引用讓編譯器知道何時可使用移動語義。
比如,上面的示例中對象one是左值,與左值引用匹配,而表達式one + three是右值,與右值引用匹配。
2. 為自定義類編寫移動構造函數,使其具備所需的行為能力。
【3】移動語義之移動賦值運算符
適用於構造函數的移動語義考慮也適用於賦值運算符。
示例1. 只有賦值運算符函數
1 // 只有復制賦值運算符 2 #include <iostream> 3 using namespace std; 4 5 // interface 6 class Useless 7 { 8 private: 9 int n; // number of elements 10 char * pc; // pointer to data 11 static int ct; // number of objects 12 void ShowObject() const; 13 14 public: 15 Useless(); 16 explicit Useless(int k); 17 Useless(int k, char ch); 18 Useless(const Useless & f); // regular copy constructor 19 Useless(Useless && f); // move constructor 20 Useless & operator=(const Useless & f); // copy assignment 21 ~Useless(); 22 Useless operator+(const Useless & f)const; 23 void ShowData() const; 24 }; 25 26 // implementation 27 int Useless::ct = 0; 28 29 Useless::Useless() 30 { 31 ++ct; 32 n = 0; 33 pc = nullptr; 34 cout << "default constructor called; number of objects: " << ct << endl; 35 ShowObject(); 36 } 37 38 Useless::Useless(int k) : n(k) 39 { 40 ++ct; 41 cout << "Useless(int k) constructor called; number of objects: " << ct << endl; 42 pc = new char[n]; 43 ShowObject(); 44 } 45 46 Useless::Useless(int k, char ch) : n(k) 47 { 48 ++ct; 49 cout << "Useless(int k, char ch) constructor called; number of objects: " << ct << endl; 50 pc = new char[n]; 51 for (int i = 0; i < n; i++) 52 pc[i] = ch; 53 ShowObject(); 54 } 55 56 Useless::Useless(const Useless & f) : n(f.n) 57 { 58 ++ct; 59 cout << "copy const called; number of objects: " << ct << endl; 60 pc = new char[n]; 61 for (int i = 0; i < n; i++) 62 pc[i] = f.pc[i]; 63 ShowObject(); 64 } 65 66 Useless::Useless(Useless && f) : n(f.n) 67 { 68 ++ct; 69 cout << "move constructor called; number of objects: " << ct << endl; 70 pc = f.pc; // steal address 71 f.pc = nullptr; // give old object nothing in return 72 f.n = 0; 73 ShowObject(); 74 } 75 76 Useless & Useless::operator=(const Useless & f) 77 { 78 cout << "copy assignment operator= called;\n"; 79 if (this == &f) 80 return *this; 81 delete []pc; 82 n = f.n; 83 pc = new char[n]; 84 for (int i = 0; i < n; ++i) 85 pc[i] = f.pc[i]; 86 return *this; 87 } 88 89 Useless::~Useless() 90 { 91 cout << "destructor called; deleted object: "; 92 ShowObject(); 93 delete [] pc; 94 cout << "objects left: " << --ct << endl; 95 } 96 97 Useless Useless::operator+(const Useless & f)const 98 { 99 cout << "Entering operator+()\n"; 100 Useless temp = Useless(n + f.n); 101 for (int i = 0; i < n; i++) 102 temp.pc[i] = pc[i]; 103 for (int i = n; i < temp.n; i++) 104 temp.pc[i] = f.pc[i - n]; 105 cout << "temp object:\n"; 106 cout << "Leaving operator+()\n"; 107 return temp; 108 } 109 110 void Useless::ShowObject() const 111 { 112 cout << "Number of elements: " << n; 113 cout << " Data address: " << (void *) pc << endl; 114 } 115 116 void Useless::ShowData() const 117 { 118 if (0 == n) 119 { 120 cout << "(object empty)"; 121 } 122 else 123 { 124 for (int i = 0; i < n; i++) 125 cout << pc[i]; 126 } 127 cout << endl; 128 } 129 130 // application 131 int main() 132 { 133 { 134 Useless one(10, 'x'); 135 Useless two = one + one; // calls move constructor 136 cout << "Object one: "; 137 one.ShowData(); 138 cout << "Object two: "; 139 two.ShowData(); 140 Useless three, four; 141 cout << "three = one \n"; 142 three = one; // automatic copy assignment 143 cout << "Now Object three = "; 144 three.ShowData(); 145 cout << "And Object one = "; 146 one.ShowData(); 147 cout << "four = one + two \n"; 148 four = one + two; // automatic move assignment 149 cout << "Now Object four = "; 150 four.ShowData(); 151 cout << "four = move(one)\n"; 152 four = std::move(one); // forced move assignment 153 cout << "Now Object four = "; 154 four.ShowData(); 155 cout << "And Object one = "; 156 one.ShowData(); 157 } 158 cin.get(); 159 } 160 161 // run out 162 /* 163 Useless(int k, char ch) constructor called; number of objects: 1 164 Number of elements: 10 Data address: 00794910 165 Entering operator+() 166 Useless(int k) constructor called; number of objects: 2 167 Number of elements: 20 Data address: 00794958 168 temp object: 169 Leaving operator+() 170 move constructor called; number of objects: 3 171 Number of elements: 20 Data address: 00794958 172 destructor called; deleted object: Number of elements: 0 Data address: 00000000 173 objects left: 2 174 Object one: xxxxxxxxxx 175 Object two: xxxxxxxxxxxxxxxxxxxx 176 default constructor called; number of objects: 3 177 Number of elements: 0 Data address: 00000000 178 default constructor called; number of objects: 4 179 Number of elements: 0 Data address: 00000000 180 three = one 181 copy assignment operator= called; 182 Now Object three = xxxxxxxxxx 183 And Object one = xxxxxxxxxx 184 four = one + two 185 Entering operator+() 186 Useless(int k) constructor called; number of objects: 5 187 Number of elements: 30 Data address: 007949F0 188 temp object: 189 Leaving operator+() 190 move constructor called; number of objects: 6 191 Number of elements: 30 Data address: 007949F0 192 destructor called; deleted object: Number of elements: 0 Data address: 00000000 193 objects left: 5 194 copy assignment operator= called; 195 destructor called; deleted object: Number of elements: 30 Data address: 007949F0 196 197 objects left: 4 198 Now Object four = xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 199 four = move(one) 200 copy assignment operator= called; 201 Now Object four = xxxxxxxxxx 202 And Object one = xxxxxxxxxx 203 destructor called; deleted object: Number of elements: 10 Data address: 007949F0 204 205 objects left: 3 206 destructor called; deleted object: Number of elements: 10 Data address: 007949A8 207 208 objects left: 2 209 destructor called; deleted object: Number of elements: 20 Data address: 00794958 210 211 objects left: 1 212 destructor called; deleted object: Number of elements: 10 Data address: 00794910 213 214 objects left: 0 215 216 */
由於沒有寫移動賦值運算符函數,以上執行結果不做分析(可對比下面的分析更充分的理解)。
示例2. 有賦值運算符函數,有移動賦值運算符函數
1 // 有復制賦值運算符 且 有移動賦值運算符 2 // useless.cpp -- an otherwise useless class with move semantics 3 #include <iostream> 4 using namespace std; 5 6 // interface 7 class Useless 8 { 9 private: 10 int n; // number of elements 11 char * pc; // pointer to data 12 static int ct; // number of objects 13 void ShowObject() const; 14 15 public: 16 Useless(); 17 explicit Useless(int k); 18 Useless(int k, char ch); 19 Useless(const Useless & f); // regular copy constructor 20 Useless(Useless && f); // move constructor 21 Useless & operator=(const Useless & f); // copy assignment 22 Useless & operator=(Useless && f); // move assignment 23 ~Useless(); 24 Useless operator+(const Useless & f)const; 25 void ShowData() const; 26 }; 27 28 // implementation 29 int Useless::ct = 0; 30 31 Useless::Useless() 32 { 33 ++ct; 34 n = 0; 35 pc = nullptr; 36 cout << "default constructor called; number of objects: " << ct << endl; 37 ShowObject(); 38 } 39 40 Useless::Useless(int k) : n(k) 41 { 42 ++ct; 43 cout << "Useless(int k) constructor called; number of objects: " << ct << endl; 44 pc = new char[n]; 45 ShowObject(); 46 } 47 48 Useless::Useless(int k, char ch) : n(k) 49 { 50 ++ct; 51 cout << "Useless(int k, char ch) constructor called; number of objects: " << ct << endl; 52 pc = new char[n]; 53 for (int i = 0; i < n; i++) 54 pc[i] = ch; 55 ShowObject(); 56 } 57 58 Useless::Useless(const Useless & f) : n(f.n) 59 { 60 ++ct; 61 cout << "copy const called; number of objects: " << ct << endl; 62 pc = new char[n]; 63 for (int i = 0; i < n; i++) 64 pc[i] = f.pc[i]; 65 ShowObject(); 66 } 67 68 Useless::Useless(Useless && f) : n(f.n) 69 { 70 ++ct; 71 cout << "move constructor called; number of objects: " << ct << endl; 72 pc = f.pc; // steal address 73 f.pc = nullptr; // give old object nothing in return 74 f.n = 0; 75 ShowObject(); 76 } 77 78 Useless & Useless::operator=(const Useless & f) 79 { 80 cout << "copy assignment operator= called;\n"; 81 if (this == &f) 82 return *this; 83 delete []pc; 84 n = f.n; 85 pc = new char[n]; 86 for (int i = 0; i < n; ++i) 87 pc[i] = f.pc[i]; 88 return *this; 89 } 90 91 Useless & Useless::operator=(Useless && f) 92 { 93 cout << "move assignment operator= called;\n"; 94 if (this == &f) 95 return *this; 96 delete []pc; 97 n = f.n; 98 pc = f.pc; 99 f.n = 0; 100 f.pc = nullptr; 101 return *this; 102 } 103 104 Useless::~Useless() 105 { 106 cout << "destructor called; deleted object: "; 107 ShowObject(); 108 delete [] pc; 109 cout << "objects left: " << --ct << endl; 110 } 111 112 Useless Useless::operator+(const Useless & f)const 113 { 114 cout << "Entering operator+()\n"; 115 Useless temp = Useless(n + f.n); 116 for (int i = 0; i < n; i++) 117 temp.pc[i] = pc[i]; 118 for (int i = n; i < temp.n; i++) 119 temp.pc[i] = f.pc[i - n]; 120 cout << "temp object:\n"; 121 cout << "Leaving operator+()\n"; 122 return temp; 123 } 124 125 void Useless::ShowObject() const 126 { 127 cout << "Number of elements: " << n; 128 cout << " Data address: " << (void *) pc << endl; 129 } 130 131 void Useless::ShowData() const 132 { 133 if (0 == n) 134 { 135 cout << "(object empty)"; 136 } 137 else 138 { 139 for (int i = 0; i < n; i++) 140 cout << pc[i]; 141 } 142 cout << endl; 143 } 144 145 // application 146 int main() 147 { 148 { 149 Useless one(10, 'x'); 150 Useless two = one + one; // calls move constructor 151 cout << "Object one: "; 152 one.ShowData(); 153 cout << "Object two: "; 154 two.ShowData(); 155 Useless three, four; 156 cout << "three = one \n"; 157 three = one; // automatic copy assignment 158 cout << "Now Object three = "; 159 three.ShowData(); 160 cout << "And Object one = "; 161 one.ShowData(); 162 cout << "four = one + two \n"; 163 four = one + two; // automatic move assignment 164 cout << "Now Object four = "; 165 four.ShowData(); 166 cout << "four = move(one)\n"; 167 four = std::move(one); // forced move assignment 168 cout << "Now Object four = "; 169 four.ShowData(); 170 cout << "And Object one = "; 171 one.ShowData(); 172 } 173 cin.get(); 174 } 175 /* 176 Useless(int k, char ch) constructor called; number of objects: 1 177 Number of elements: 10 Data address: 00204910 178 Entering operator+() 179 Useless(int k) constructor called; number of objects: 2 180 Number of elements: 20 Data address: 00204958 181 temp object: 182 Leaving operator+() 183 move constructor called; number of objects: 3 184 Number of elements: 20 Data address: 00204958 185 destructor called; deleted object: Number of elements: 0 Data address: 00000000 186 objects left: 2 187 Object one: xxxxxxxxxx 188 Object two: xxxxxxxxxxxxxxxxxxxx 189 default constructor called; number of objects: 3 190 Number of elements: 0 Data address: 00000000 191 default constructor called; number of objects: 4 192 Number of elements: 0 Data address: 00000000 193 three = one 194 copy assignment operator= called; 195 Now Object three = xxxxxxxxxx 196 And Object one = xxxxxxxxxx 197 four = one + two 198 Entering operator+() 199 Useless(int k) constructor called; number of objects: 5 200 Number of elements: 30 Data address: 002049F0 201 temp object: 202 Leaving operator+() 203 move constructor called; number of objects: 6 204 Number of elements: 30 Data address: 002049F0 205 destructor called; deleted object: Number of elements: 0 Data address: 00000000 206 objects left: 5 207 move assignment operator= called; 208 destructor called; deleted object: Number of elements: 0 Data address: 00000000 209 objects left: 4 210 Now Object four = xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 211 four = move(one) 212 move assignment operator= called; 213 Now Object four = xxxxxxxxxx 214 And Object one = (object empty) 215 destructor called; deleted object: Number of elements: 10 Data address: 00204910 216 217 objects left: 3 218 destructor called; deleted object: Number of elements: 10 Data address: 002049A8 219 220 objects left: 2 221 destructor called; deleted object: Number of elements: 20 Data address: 00204958 222 223 objects left: 1 224 destructor called; deleted object: Number of elements: 0 Data address: 00000000 225 objects left: 0 226 */
運作結果分析如下:

移動賦值運算符函數如下:
1 Useless & Useless::operator=(Useless && f) 2 { 3 cout << "move assignment operator= called;\n"; 4 if (this == &f) 5 return *this; 6 delete []pc; 7 n = f.n; 8 pc = f.pc; 9 f.n = 0; 10 f.pc = nullptr; 11 return *this; 12 }
移動賦值運算符刪除目標對象中的原始數據,並將源對象的所有權轉讓給目標對象。不能讓多個指針指向相同的數據(同上移動構造函數的原理)。
【4】強制移動
移動構造函數和移動賦值運算符使用右值。如果想要強制移動,即要讓它們使用左值作為實參,可使用運算符static_cast<>將對象的類型強制轉換為Useless &&。
但C++11提供了更簡單的方式——使用頭文件utility中聲明的函數std::move()。如上示例中已使用。
通過例子比較可知,函數std::move()並非一定會導致移動操作。表達式std::move(one)是右值,因此上例賦值語句將調用其移動賦值運算符(前提條件是定義了移動賦值運算符)。
如果沒有定義移動賦值運算符,編譯器將使用復制賦值運算符。如果也沒有定義復制賦值運算符,將根本不允許上例的賦值。
Good Good Study, Day Day Up.
順序 選擇 循環 總結
