移动语义

移动语义 Move Semantics

为了减少拷贝构造函数在堆上重新分配内存的时间,并且构造函数的参数是临时值,

使用右值引用来构造类,此构造函数的实现要把原来的资源指针设置为空nullptr,delete才不会出错

在Entity初始化列表中,声明了一个类型是右值引用的左值,所以要通过std::move()强行转化为右值引用,

也可以直接写强制类型转换(String&&)

然后对比测试一下执行时间

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
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
#include <iostream>
#define ps(str) std::cout << str;
class Timer
{
public:
Timer(const char* name)
:m_Name(name), m_Stopped(false)
{
m_StartTimepoint = std::chrono::high_resolution_clock::now();
}
~Timer()
{
if (!m_Stopped)
{
Stop();
}
}
void Stop()
{
auto endTimePoint = std::chrono::high_resolution_clock::now();

long long start = std::chrono::time_point_cast<std::chrono::microseconds>(m_StartTimepoint).time_since_epoch().count();
long long end = std::chrono::time_point_cast<std::chrono::microseconds>(endTimePoint).time_since_epoch().count();
std::cout << "花费时间: " << (end - start) << " ms\n";
m_Stopped = true;
}
private:
const char* m_Name;
std::chrono::time_point<std::chrono::steady_clock> m_StartTimepoint;
bool m_Stopped;

};
class String
{
public:
//默认构造函数
String() = default;
//接受const char*构造函数
String(const char* string)
{
printf("Created!\n");
m_Size = strlen(string);
m_Data = new char[m_Size];
memcpy(m_Data,string,m_Size);
}

//90%接受Lvalue的拷贝构造函数
String(const String& other)
{
printf("Copied!\n");
m_Size = other.m_Size;
m_Data = new char[m_Size];
memcpy(m_Data,other.m_Data,m_Size);
}

//接受Rvalue 的构造函数,注意实现要 窃取资源
String(String&& other) noexcept
{
printf("Moved!\n");
m_Size = other.m_Size;
m_Data = other.m_Data;

//偷了他的资源,避免delete error,所以伪装一下
other.m_Size = 0;
other.m_Data = nullptr;
}

//析构函数
~String()
{
ps("Destroyed!\n");
delete m_Data;
}

public:
void Print()
{
for (uint32_t i = 0; i < m_Size; i++)
{
printf("%c",m_Data[i]);
}
printf("\n");
}
private:
char* m_Data;
uint32_t m_Size;
};

class Entity
{
public:
Entity() = default;

Entity(const String& name)
:m_Name((name)) //name 既可能时L value,又可能是R value
{}

Entity(String&& name)
:m_Name(std::move(name)) //**声明变量name是一个Rvalue而不是Lvalue,不然 m_Name(name) 调用的还是Lvalue构造函数
{}

~Entity() = default;

void PrintName()
{
m_Name.Print();
}
private:
String m_Name;
};

int main()
{
/*
const char* -> String : String(const char *)
String -> String : Entity(const String& name) String(const String&)
*/
{

Timer timer("1");
String temp("1234567890\
1234567890\
1234567890\
1234567890");
Entity entity1(temp);
}

/*
* const char* -> String : String(const char *)
* String-> String : Entity(String&& name) String(String && name)
*/

{
Timer timer("2");
Entity entity("1234567890\
1234567890\
1234567890\
1234567890");
}

//entity.PrintName();

std::cin.get();
}

只要某个类的成员变量是在堆上分配内存的,那么就可以写接受右值引用的构造函数

移动赋值 Move Assignment

创建新变量时使用=赋值符号实际上调用的是构造函数,

而对已经存在的变量使用赋值运算符实际上是调用xx& operator =()函数

重载赋值函数时,因为拿到是RValue的资源,所以我们先要正确的释放自己在堆上的资源

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
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
#include "../src/include/local.h"
#define ps(str) std::cout << str;
class Timer
{
public:
Timer(const char* name)
:m_Name(name), m_Stopped(false)
{
m_StartTimepoint = std::chrono::high_resolution_clock::now();
}
~Timer()
{
if (!m_Stopped)
{
Stop();
}
}
void Stop()
{
auto endTimePoint = std::chrono::high_resolution_clock::now();

long long start = std::chrono::time_point_cast<std::chrono::microseconds>(m_StartTimepoint).time_since_epoch().count();
long long end = std::chrono::time_point_cast<std::chrono::microseconds>(endTimePoint).time_since_epoch().count();
std::cout << "花费时间: " << (end - start) << " ms\n";
m_Stopped = true;
}
private:
const char* m_Name;
std::chrono::time_point<std::chrono::steady_clock> m_StartTimepoint;
bool m_Stopped;

};
class String
{
public:
//默认构造函数
String() = default;
//接受const char*构造函数
String(const char* string)
{
printf("Created!\n");
m_Size = strlen(string);
m_Data = new char[m_Size];
memcpy(m_Data,string,m_Size);
}

//90接受Lvalue的拷贝构造函数
String(const String& other)
{
printf("Copied!\n");
m_Size = other.m_Size;
m_Data = new char[m_Size];
memcpy(m_Data,other.m_Data,m_Size);
}

//接受Rvalue 的构造函数,注意实现要 窃取资源
String(String&& other) noexcept
{
printf("Moved! Constructor\n");
m_Size = other.m_Size;
m_Data = other.m_Data;

//偷了他的资源,避免delete error,所以伪装一下
other.m_Size = 0;
other.m_Data = nullptr;
}

String& operator=(const String& other)
{//要注意我自己已经在堆上分配了内存
//copy
delete[] m_Data;
m_Size = other.m_Size;
m_Data = new char[m_Size];
memcpy(m_Data, other.m_Data, m_Size);

return *this;
}

String& operator=(String&& other) noexcept
{
//避免自己=自己,造成资源转移死循环
if (this == &other)
{
ps("相同\n");
return *this;
}

printf("Move assignment\n");
delete[] m_Data;

m_Size = other.m_Size;
m_Data = other.m_Data;

other.m_Size = 0;
other.m_Data = nullptr;

return *this;
}

//析构函数
~String()
{
ps("Destroyed!\n");
delete m_Data;
}

public:
void Print()
{
for (uint32_t i = 0; i < m_Size; i++)
{
printf("%c",m_Data[i]);
}
printf("\n");
}
private:
char* m_Data;
uint32_t m_Size;
};

class Entity
{
public:
Entity() = default;

Entity(const String& name)
:m_Name((name)) //name 既可能时L value,又可能是R value
{}

Entity(String&& name)
:m_Name(std::move(name)) //**声明变量name是一个Rvalue而不是Lvalue,不然 m_Name(name) 调用的还是Lvalue构造函数
{}

~Entity() = default;

void PrintName()
{
m_Name.Print();
}
private:
String m_Name;
};

int main()
{
String apple = "Apple";
String dest;
dest = (String&&)(apple);
dest.Print();
apple.Print();

std::cin.get();
}

std::array<,>

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
template<typename T,size_t N>
class Array
{
public:
constexpr int Size() const
{
return N;
}
T& operator[](size_t index)
{
return m_Data[index];
}
const T& operator[](size_t index)const
{
return m_Data[index];
}
private:
T m_Data[N];
};

int main()
{
int size = 5;
Array<int, 5> data;

const auto& arrayReference = data;
for (int i = 0; i < data.Size(); i++)
{
//arrayReference[i] = 2;
std::cout << arrayReference[i] << std::endl;
}

std::cin.get();
}

Iterator

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
#include <unordered_map>

int main()
{
using ScoreMap = std::unordered_map<std::string, int>;
ScoreMap map;
map["String"] = 5;
map["C++"] = 1;
for (ScoreMap::iterator it = map.begin(); it != map.end(); it++)
{
const auto& key = it->first;
const auto& value = it->second;

std::cout << key << ", " << value<< std::endl;
}

for (auto kv : map)
{
auto& key = kv.first;
auto& value = kv.second;
std::cout << key << ", " << value << std::endl;
}

//c++17 struct bind
for (auto[key,value] : map)
{
std::cout << key << ", " << value << std::endl;
}
std::cin.get();
}

制作Iterator

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
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
template<typename Vector>
class VectorIterator
{
public:
/*告诉MSVC这是类型而不是类的成员变量*/
using ValueType = typename Vector::ValueType;
using PointerType = ValueType*;
using ReferenceType = ValueType&;
public:
VectorIterator(PointerType ptr)
:m_Ptr(ptr) {}

VectorIterator& operator++()
{
m_Ptr++;
return *this;
}

VectorIterator operator++(int)
{
VectorIterator tmp = *this;
++(*this);//后置自增由前置自增实现
return tmp;
}

VectorIterator& operator--()
{
m_Ptr--;
return *this;
}

VectorIterator operator--(int)
{
VectorIterator tmp = *this;
--(*this);//后置自增由前置自增实现
return tmp;
}

ReferenceType operator[](int index)
{
return *(m_Ptr + index);
}

PointerType operator->()
{
return m_Ptr;
}

ReferenceType operator*()
{
return *m_Ptr;
}

bool operator==(const VectorIterator& other) const
{
return m_Ptr == other.m_Ptr;
}

bool operator!=(const VectorIterator& other) const
{
return !(*this == other);
}
private:
PointerType m_Ptr;
};

template<typename T>
class Vector
{
public:
using ValueType = T;
using Iterator = VectorIterator<Vector<T>>;
public:
Vector()
{
ReAlloc(2);
}

~Vector()
{
delete[] m_Data;
}

void PushBack(const T& value)
{
if (m_Size >= m_Capacity)
ReAlloc(m_Capacity + m_Capacity / 2);
m_Data[m_Size++] = value;
}

void PushBack(T&& value)
{
if (m_Size >= m_Capacity)
ReAlloc(m_Capacity + m_Capacity / 2);
m_Data[m_Size] = std::move(value); //获得Lvalue接受的Rvalue
m_Size++;
}

template<typename... Args>
T& EmplaceBack(Args&&... args)
{
if (m_Size >= m_Capacity)
ReAlloc(m_Capacity + m_Capacity / 2);

m_Data[m_Size] = T(std::forward<Args>(args)...);
return m_Data[m_Size++];
}

void PopBack()
{
if (m_Size > 0)
{
m_Size--;
m_Data[m_Size].~T();
}
}

void Clear()
{
for (size_t i = 0; i < m_Size; i++)
m_Data[i].~T();
m_Size = 0;
}

T& operator[](size_t index)
{
//索引下标超出 容器元素的个数
if (index >= m_Size)
{

}
return m_Data[index];
}

const T& operator[](size_t index) const
{
if (index >= m_Size)
{

}
return m_Data[index];
}

size_t Size() const { return m_Size; }

Iterator begin()
{
return Iterator(m_Data);
}

Iterator end()
{
return Iterator(m_Data + m_Size);
}
private:
void ReAlloc(size_t newCapacity)
{
T* newBlock = new T[newCapacity];

//如果newCapacity值小于当前元素个数
if (newCapacity < m_Size)
m_Size = newCapacity;

for (size_t i = 0; i < m_Size; i++)
newBlock[i] = std::move(m_Data[i]); //

delete[] m_Data;
m_Data = newBlock;
m_Capacity = newCapacity;
}
private:
T* m_Data = nullptr;

size_t m_Size = 0; //元素的个数
size_t m_Capacity = 0;//容器的容量
};

int main()
{
Vector<int> values;
values.EmplaceBack(1);
values.EmplaceBack(2);
values.EmplaceBack(9);
values.EmplaceBack(8);
values.EmplaceBack(76);
values.EmplaceBack(7);

std::cout << "Not using iterator:\n";
for (size_t i = 0; i < values.Size(); i++)
{
std::cout << values[i] << std::endl;
}

std::cout << "Range-based for loop:\n";
for (int value : values)
{
std::cout << value << std::endl;
}
std::cin.get();
}