移動建構子與移動指派運算子
Prerequisites:
把RVO關閉後,才能進行以下的範例。
目的
以下的拷貝函式,針對指標(m_ptr)動態分配記憶體位址,將來源(source)物件指標內容拷貝到新分配的記憶體位址。
1
2
3
4
5
6
7
8
9
10
11
Student(const Student &s) {
//判斷來源(source)m_ptr是否null
if(s.m_ptr) {
//動態分配記憶體位址
m_ptr = new int;
//拷貝來源物件m_ptr指標到新的記憶體位址
memcpy(m_ptr, src.m_ptr, sizeof(int));
} else {
m_ptr = nullptr;
}
}
以上程式碼若拷貝的容量很大,將會花費許多時間在拷貝資料。
移動建構子(Move constructor)與移動指派運算子(Move assignment operator)主要針對右值進行指標移動,因為右值是一個臨時物件,通常使用完畢立即記憶體釋放,移動建構子與移動指派運算子是不釋放臨時物件記憶體,把指標指向臨時物件(來源物件)的記憶體位址,省略以上程式碼拷貝動作。
語法
移動建構子(Move constructor)與移動指派運算子(Move assignment operator)的參數都是右值(r-value),因為要刪除來源物件指標(有修改的動作),所以參數不用const,拷貝與指派運算子則是不會修改來源物件。
移動建構子(Move constructor)
類別名(類別名&& 來源物件) {
....
}
移動指派運算子(Move assignment operator)
類別名& operator=(類別名&& 來源物件) {
......
return *this;
}
指標轉移語法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Student(Student&& src) {
cout << "移動建構子" << endl;
//把來源的指標移到m_ptr
m_ptr = src.m_ptr;
//把來源的指標設nullptr
src.m_ptr = nullptr;
}
Student& operator=(Student&& src) {
cout << "移動指派運算子" << endl;
//把來源的指標移到m_ptr
m_ptr = src.m_ptr;
//把來源的指標設nullptr
src.m_ptr = nullptr;
return *this;
}
使用右值移動建構子
第一次右值移動建構子:return s;這個臨時對象被移動到一個中間的無名臨時對象中,觸發了第一次的右值移動建構子。
第二次右值移動建構子:接著,這個中間的無名臨時對象被移動到 s4 中,觸發了第二次的右值移動建構子。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
#include <iostream>
#include <functional>
using namespace std;
class Student {
public:
int* m_ptr;
public:
Student() {
cout << "建構子" << endl;
//初始化成員變數
m_ptr = nullptr;
}
Student(const Student &src) {
cout << "左值拷貝函式" << endl;
//判斷來源(source)m_ptr不是null
if(src.m_ptr) {
//動態分配記憶體位址
m_ptr = new int;
//拷貝來源物件m_ptr指標到新的記憶體位址
memcpy(m_ptr, src.m_ptr, sizeof(int));
} else {
m_ptr = nullptr;
}
}
Student& operator=(const Student& src) {
cout << "左值指派運算子" << endl;
//如果來源物件的m_ptr指標是空
if(src.m_ptr == nullptr) {
//要把目的物件清空(如果目的物件不是空的話)
if(m_ptr != nullptr) {
delete m_ptr;
m_ptr = nullptr;
}
} else {
//如果目的物件為空
if(m_ptr == nullptr) {
//動態分配記憶體
m_ptr = new int;
memset(m_ptr, 0, sizeof(int));
}
//記憶體的值拷貝
memcpy(m_ptr, src.m_ptr, sizeof(int));
}
return *this;
}
Student(Student&& src) {
cout << "右值移動建構子" << endl;
//把來源的指標移到m_ptr
m_ptr = src.m_ptr;
//把來源的指標設nullptr
src.m_ptr = nullptr;
}
Student& operator=(Student&& src) {
cout << "右值移動指派運算子" << endl;
//把來源的指標移到m_ptr
m_ptr = src.m_ptr;
//把來源的指標設nullptr
src.m_ptr = nullptr;
return *this;
}
~Student() {
delete m_ptr;
m_ptr = nullptr;
cout << "解構子" << endl;
}
void print() {
//印出記憶體位址
cout << "m_ptr address:" << &m_ptr << endl;
cout << "m_ptr value = " << *m_ptr << endl;
}
};
int main() {
//右值指派運算子
Student s4 = [] {
Student s;
s.m_ptr = new int;
*s.m_ptr = 200;
return s;
}();
cout << "s4 mptr = " << *s4.m_ptr << endl;
return 0;
}
建構子
右值移動建構子
解構子
右值移動建構子
解構子
s4 mptr = 200
解構子