@@ -11,12 +11,214 @@ individually or as a whole.
11
11
Let me know if this is useful to anyone or if there are any areas you want
12
12
covered. This is (probably forever) a work in progress.
13
13
14
- [ How to use std::unique_ptr ] ( unique_ptr /README.md)
14
+ [ How to use std::move ] ( std_move /README.md)
15
15
16
- [ How to use std::shared_ptr ] ( shared_ptr /README.md)
16
+ [ How to use std::unique_ptr ] ( std_unique_ptr /README.md)
17
17
18
- [ How to make your own wrapper around std::shared_ptr] ( shared_ptr_wrapper /README.md)
18
+ [ How to use std::shared_ptr] ( std_shared_ptr /README.md)
19
19
20
+ [ How to make your own wrapper around std::shared_ptr] ( std_shared_ptr_wrapper/README.md )
21
+
22
+ How to use std::move
23
+ ====================
24
+
25
+ std::move is a nifty way to avoid the use of temporaries when doing copies
26
+ of data. For example if you do:
27
+
28
+ ``` C++
29
+ template <class T > void simpleswap (T& a, T& b) {
30
+ T tmp = b;
31
+ b = a;
32
+ a = b;
33
+ }
34
+ ```
35
+
36
+ You will see the copy constructor invoked. Not a huge deal for small classes,
37
+ but if you are dealing with a vector, that could be very expensive.
38
+
39
+ A better approach is to use std::move e.g.
40
+
41
+ ```C++
42
+ template<class T> void simpleswap(T& a, T& b) {
43
+ T tmp { std::move(a) }; // invokes move constructor
44
+ a = std::move(b); // invokes move assignment
45
+ b = std::move(tmp); // invokes move assignment
46
+ }
47
+ ```
48
+
49
+ Now the question is, how do we implement std::move on our own classes.
50
+ To use this you will see a new form of constructor with the "&&" syntax.
51
+ The example below will create our own vector class and then implement
52
+ the move constructor to transfer elements across. The old vector will
53
+ be left empty.
54
+
55
+ Note that std::vector already does this approach; we're just applying
56
+ the same ideas to a custom class.
57
+ ``` C++
58
+ #include < iostream>
59
+ #include < string>
60
+ #include < sstream> // std::stringstream
61
+ #include < algorithm> // std::move
62
+ #include " ../common/common.h"
63
+
64
+ template <class T > class MyVector {
65
+ private:
66
+ T * data;
67
+ size_t maxlen;
68
+ size_t currlen;
69
+ public:
70
+ MyVector<T > () : data (nullptr), maxlen(0), currlen(0) {
71
+ std::cout << "default constructor " << to_string() << std::endl;
72
+ }
73
+ MyVector<T > (int maxlen) : data (new T [ maxlen] ),
74
+ maxlen(maxlen),
75
+ currlen(0) {
76
+ std::cout << "new " << to_string() << std::endl;
77
+ }
78
+ MyVector<T > (const MyVector& o) {
79
+ std::cout << "copy constructor called for " << o.to_string() << std::endl;
80
+ data = new T [ o.maxlen] ;
81
+ maxlen = o.maxlen;
82
+ currlen = o.currlen;
83
+ std::copy(o.data, o.data + o.maxlen, data);
84
+ std::cout << "copy constructor result is " << to_string() << std::endl;
85
+ }
86
+ MyVector<T > (MyVector<T >&& o) {
87
+ std::cout << "std::move called for " << o.to_string() << std::endl;
88
+ data = o.data;
89
+ maxlen = o.maxlen;
90
+ currlen = o.currlen;
91
+ o.data = nullptr;
92
+ o.maxlen = 0;
93
+ o.currlen = 0;
94
+ std::cout << "std::move result is " << to_string() << std::endl;
95
+ }
96
+ ~ MyVector<T > () {
97
+ std::cout << "delete " << to_string() << std::endl;
98
+ }
99
+ void push_back (const T& i) {
100
+ if (currlen >= maxlen) {
101
+ maxlen * = 2;
102
+ auto newdata = new T [ maxlen] ;
103
+ std::copy(data, data + currlen, newdata);
104
+ if (data) {
105
+ delete[ ] data;
106
+ }
107
+ data = newdata;
108
+ }
109
+ data[ currlen++] = i;
110
+ std::cout << "push_back called " << to_string() << std::endl;
111
+ }
112
+ friend std::ostream& operator<<(std::ostream &os, const MyVector<T >& o) {
113
+ auto s = o.data;
114
+ auto e = o.data + o.currlen;;
115
+ while (s < e) {
116
+ os << "[ " << * s << "] ";
117
+ s++;
118
+ }
119
+ return os;
120
+ }
121
+ std::string to_string (void) const {
122
+ auto address = static_cast<const void* >(this);
123
+ std::stringstream ss;
124
+ ss << address;
125
+
126
+ std::string elems;
127
+ auto s = data;
128
+ auto e = data + currlen;;
129
+ while (s < e) {
130
+ elems += std::to_string(*s);
131
+ s++;
132
+ if (s < e) {
133
+ elems += ",";
134
+ }
135
+ }
136
+
137
+ return " MyVector(" + ss.str() +
138
+ " , currlen=" + std::to_string(currlen) +
139
+ " , maxlen=" + std::to_string(maxlen) +
140
+ " elems=[" + elems + " ])" ;
141
+ }
142
+ };
143
+
144
+ int main () {
145
+ // Create a custom vector class:
146
+ auto vec1 = new MyVector<int>(1);
147
+ vec1->push_back(10);
148
+ vec1->push_back(11);
149
+ std::cout << "vec1: " << *vec1 << std::endl;
150
+
151
+ // Create a new copy of vec1, vec2 via copy constructor (&):
152
+ auto vec2 = *vec1;
153
+ std::cout << "vec2: " << vec2 << std::endl;
154
+
155
+ // Check we can append onto the copied vector:
156
+ vec2.push_back(12);
157
+ vec2.push_back(13);
158
+ std::cout << "vec2: " << vec2 << std::endl;
159
+
160
+ // Create a new vector from vec1, vec3 via the move constructor (&&&):
161
+ auto vec3 = std::move(*vec1);
162
+ std::cout << "vec3: " << vec3 << std::endl;
163
+
164
+ // Check we can append onto the std:move'd vector:
165
+ vec3.push_back(14);
166
+ vec3.push_back(15);
167
+ std::cout << "vec3: " << vec3 << std::endl;
168
+
169
+ // Destroy the old vector, vec1. It has no invalid elems:
170
+ delete vec1;
171
+
172
+ // End, expect vec2 and vec3 destroy:
173
+ // End
174
+ }
175
+ ```
176
+ To build:
177
+ <pre >
178
+ cd std_move
179
+ rm -f *.o example
180
+ c++ -std=c++2a -Werror -g -ggdb3 -Wall -c -o main.o main.cpp
181
+ c++ main.o -o example
182
+ ./example
183
+ </pre >
184
+ Expected output:
185
+ <pre >
186
+
187
+ # Create a custom vector class:
188
+ new MyVector(0x7fbbf64029b0, currlen=0, maxlen=1 elems=[])
189
+ push_back called MyVector(0x7fbbf64029b0, currlen=1, maxlen=1 elems=[10])
190
+ push_back called MyVector(0x7fbbf64029b0, currlen=2, maxlen=2 elems=[10,11])
191
+ vec1: [10][11]
192
+
193
+ # Create a new copy of vec1, vec2 via copy constructor (&):
194
+ copy constructor called for MyVector(0x7fbbf64029b0, currlen=2, maxlen=2 elems=[10,11])
195
+ copy constructor result is MyVector(0x7ffeed989688, currlen=2, maxlen=2 elems=[10,11])
196
+ vec2: [10][11]
197
+
198
+ # Check we can append onto the copied vector:
199
+ push_back called MyVector(0x7ffeed989688, currlen=3, maxlen=4 elems=[10,11,12])
200
+ push_back called MyVector(0x7ffeed989688, currlen=4, maxlen=4 elems=[10,11,12,13])
201
+ vec2: [10][11][12][13]
202
+
203
+ # Create a new vector from vec1, vec3 via the move constructor (&&&):
204
+ std::move called for MyVector(0x7fbbf64029b0, currlen=2, maxlen=2 elems=[10,11])
205
+ std::move result is MyVector(0x7ffeed989668, currlen=2, maxlen=2 elems=[10,11])
206
+ vec3: [10][11]
207
+
208
+ # Check we can append onto the std:move'd vector:
209
+ push_back called MyVector(0x7ffeed989668, currlen=3, maxlen=4 elems=[10,11,14])
210
+ push_back called MyVector(0x7ffeed989668, currlen=4, maxlen=4 elems=[10,11,14,15])
211
+ vec3: [10][11][14][15]
212
+
213
+ # Destroy the old vector, vec1. It has no invalid elems:
214
+ delete MyVector(0x7fbbf64029b0, currlen=0, maxlen=0 elems=[])
215
+
216
+ # End, expect vec2 and vec3 destroy:
217
+
218
+ # End
219
+ delete MyVector(0x7ffeed989668, currlen=4, maxlen=4 elems=[10,11,14,15])
220
+ delete MyVector(0x7ffeed989688, currlen=4, maxlen=4 elems=[10,11,12,13])
221
+ </pre >
20
222
How to use std::shared_ptr
21
223
==========================
22
224
@@ -108,7 +310,7 @@ int main (void)
108
310
```
109
311
To build:
110
312
<pre>
111
- cd shared_ptr
313
+ cd std_shared_ptr
112
314
rm -f *.o example
113
315
c++ -std=c++2a -Werror -g -ggdb3 -Wall -c -o main.o main.cpp
114
316
c++ main.o -o example
@@ -118,9 +320,9 @@ Expected output:
118
320
<pre>
119
321
120
322
# Create a copy constructed class and share it between two pointers:
121
- new Foo(0x7ffee88d8220 , data=foo1)
122
- copy constructor Foo(0x7fce75600a18 , data=)
123
- delete Foo(0x7ffee88d8220 , data=foo1)
323
+ new Foo(0x7ffee18fa220 , data=foo1)
324
+ copy constructor Foo(0x7f9d61c029f8 , data=)
325
+ delete Foo(0x7ffee18fa220 , data=foo1)
124
326
sptr1 ref count now 1
125
327
sptr2 ref count now 2
126
328
@@ -138,13 +340,13 @@ sptr2 ref count now 2
138
340
139
341
# Release the shared sptrs, expect foo1 to be destroyed:
140
342
sptr1 ref count now 0
141
- delete Foo(0x7fce75600a18 , data=foo1)
343
+ delete Foo(0x7f9d61c029f8 , data=foo1)
142
344
sptr2 ref count now 0
143
345
144
346
# You can also create shared pointers WITHOUT copy constructor overhead
145
- new Foo(0x7fce756009d0 , data=foo0)
146
- sptr0 = Foo(0x7fce756009d0 , data=foo0)
147
- delete Foo(0x7fce756009d0 , data=foo0)
347
+ new Foo(0x7f9d61c029b0 , data=foo0)
348
+ sptr0 = Foo(0x7f9d61c029b0 , data=foo0)
349
+ delete Foo(0x7f9d61c029b0 , data=foo0)
148
350
</pre>
149
351
How to make your own wrapper around std::shared_ptr
150
352
===================================================
@@ -275,7 +477,7 @@ int main (void)
275
477
```
276
478
To build:
277
479
<pre>
278
- cd shared_ptr_wrapper
480
+ cd std_shared_ptr_wrapper
279
481
rm -f * .o example
280
482
c++ -std=c++2a -Werror -g -ggdb3 -Wall -c -o main.o main.cpp
281
483
c++ main.o -o example
@@ -285,20 +487,20 @@ Expected output:
285
487
<pre>
286
488
287
489
# create a class and share it between two pointers:
288
- new Foo(0x7ffeea47e658 , data=foo1-data)
289
- [ foo1] : MySharedPtr::make_shared MySharedPtr(0x7ffeea47e688 ,Foo(0x7f94894029c8 , data=foo1-data))
290
- delete Foo(0x7ffeea47e658 , data=foo1-data)
490
+ new Foo(0x7ffeee280658 , data=foo1-data)
491
+ [ foo1] : MySharedPtr::make_shared MySharedPtr(0x7ffeee280688 ,Foo(0x7f8d564029c8 , data=foo1-data))
492
+ delete Foo(0x7ffeee280658 , data=foo1-data)
291
493
sptr1 ref count now 1
292
494
sptr2 ref count now 2
293
495
294
496
# release the shared sptrs, expect foo1 to be destroyed:
295
- [ foo1] : MySharedPtr::reset MySharedPtr(0x7ffeea47e688 ,Foo(0x7f94894029c8 , data=foo1-data))
497
+ [ foo1] : MySharedPtr::reset MySharedPtr(0x7ffeee280688 ,Foo(0x7f8d564029c8 , data=foo1-data))
296
498
sptr1 ref count now 0
297
- [ foo1] : MySharedPtr::reset MySharedPtr(0x7ffeea47e608 ,Foo(0x7f94894029c8 , data=foo1-data))
298
- delete Foo(0x7f94894029c8 , data=foo1-data)
499
+ [ foo1] : MySharedPtr::reset MySharedPtr(0x7ffeee280608 ,Foo(0x7f8d564029c8 , data=foo1-data))
500
+ delete Foo(0x7f8d564029c8 , data=foo1-data)
299
501
sptr2 ref count now 0
300
- [ foo1] : MySharedPtr::delete MySharedPtr(0x7ffeea47e608 )
301
- [ foo1] : MySharedPtr::delete MySharedPtr(0x7ffeea47e688 )
502
+ [ foo1] : MySharedPtr::delete MySharedPtr(0x7ffeee280608 )
503
+ [ foo1] : MySharedPtr::delete MySharedPtr(0x7ffeee280688 )
302
504
</pre>
303
505
How to use std::unique_ptr
304
506
==========================
@@ -370,7 +572,7 @@ int main (void)
370
572
```
371
573
To build:
372
574
<pre>
373
- cd unique_ptr
575
+ cd std_unique_ptr
374
576
rm -f *.o example
375
577
c++ -std=c++2a -Werror -g -ggdb3 -Wall -c -o main.o main.cpp
376
578
c++ main.o -o example
@@ -380,21 +582,21 @@ Expected output:
380
582
<pre>
381
583
382
584
# NOTE: make_unique creates a new ptr and will invoke foo1's copy constructor:
383
- new Foo(0x7ffee3843088 , data=foo1)
384
- copy constructor Foo(0x7fc4255028a0 , data=)
385
- delete Foo(0x7ffee3843088 , data=foo1)
585
+ new Foo(0x7ffee7c08088 , data=foo1)
586
+ copy constructor Foo(0x7f9258c029e0 , data=)
587
+ delete Foo(0x7ffee7c08088 , data=foo1)
386
588
387
589
# NOTE: to avoid the copy, do this:
388
- new Foo(0x7fc4255028c0 , data=foo2)
590
+ new Foo(0x7f9258c02a00 , data=foo2)
389
591
390
592
# As you cannot copy unique pointers, reassign it with move
391
593
392
594
# Let's print all the unique ptrs now
393
- uptr1 = Foo(0x7fc4255028a0 , data=foo1)
595
+ uptr1 = Foo(0x7f9258c029e0 , data=foo1)
394
596
uptr2 = nullptr
395
- uptr3 = Foo(0x7fc4255028c0 , data=foo2)
597
+ uptr3 = Foo(0x7f9258c02a00 , data=foo2)
396
598
397
599
# Expect the unique ptr data to be destroyed now
398
- delete Foo(0x7fc4255028c0 , data=foo2)
399
- delete Foo(0x7fc4255028a0 , data=foo1)
600
+ delete Foo(0x7f9258c02a00 , data=foo2)
601
+ delete Foo(0x7f9258c029e0 , data=foo1)
400
602
</pre>
0 commit comments