Skip to content

Commit e2ea40e

Browse files
committed
std::move
1 parent 647bcf6 commit e2ea40e

28 files changed

+719
-63
lines changed

Makefile

+4-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
SUBDIRS=unique_ptr shared_ptr shared_ptr_wrapper
1+
SUBDIRS=std_move \
2+
std_unique_ptr \
3+
std_shared_ptr \
4+
std_shared_ptr_wrapper
25

36
#
47
# To force clean and avoid "up to date" warning.

README.md

+231-29
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,214 @@ individually or as a whole.
1111
Let me know if this is useful to anyone or if there are any areas you want
1212
covered. This is (probably forever) a work in progress.
1313

14-
[How to use std::unique_ptr](unique_ptr/README.md)
14+
[How to use std::move](std_move/README.md)
1515

16-
[How to use std::shared_ptr](shared_ptr/README.md)
16+
[How to use std::unique_ptr](std_unique_ptr/README.md)
1717

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)
1919

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>
20222
How to use std::shared_ptr
21223
==========================
22224

@@ -108,7 +310,7 @@ int main (void)
108310
```
109311
To build:
110312
<pre>
111-
cd shared_ptr
313+
cd std_shared_ptr
112314
rm -f *.o example
113315
c++ -std=c++2a -Werror -g -ggdb3 -Wall -c -o main.o main.cpp
114316
c++ main.o -o example
@@ -118,9 +320,9 @@ Expected output:
118320
<pre>
119321
120322
# 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)
124326
sptr1 ref count now 1
125327
sptr2 ref count now 2
126328
@@ -138,13 +340,13 @@ sptr2 ref count now 2
138340
139341
# Release the shared sptrs, expect foo1 to be destroyed:
140342
sptr1 ref count now 0
141-
delete Foo(0x7fce75600a18, data=foo1)
343+
delete Foo(0x7f9d61c029f8, data=foo1)
142344
sptr2 ref count now 0
143345
144346
# 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)
148350
</pre>
149351
How to make your own wrapper around std::shared_ptr
150352
===================================================
@@ -275,7 +477,7 @@ int main (void)
275477
```
276478
To build:
277479
<pre>
278-
cd shared_ptr_wrapper
480+
cd std_shared_ptr_wrapper
279481
rm -f *.o example
280482
c++ -std=c++2a -Werror -g -ggdb3 -Wall -c -o main.o main.cpp
281483
c++ main.o -o example
@@ -285,20 +487,20 @@ Expected output:
285487
<pre>
286488

287489
# 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)
291493
sptr1 ref count now 1
292494
sptr2 ref count now 2
293495

294496
# 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))
296498
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)
299501
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)
302504
</pre>
303505
How to use std::unique_ptr
304506
==========================
@@ -370,7 +572,7 @@ int main (void)
370572
```
371573
To build:
372574
<pre>
373-
cd unique_ptr
575+
cd std_unique_ptr
374576
rm -f *.o example
375577
c++ -std=c++2a -Werror -g -ggdb3 -Wall -c -o main.o main.cpp
376578
c++ main.o -o example
@@ -380,21 +582,21 @@ Expected output:
380582
<pre>
381583
382584
# 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)
386588
387589
# NOTE: to avoid the copy, do this:
388-
new Foo(0x7fc4255028c0, data=foo2)
590+
new Foo(0x7f9258c02a00, data=foo2)
389591
390592
# As you cannot copy unique pointers, reassign it with move
391593
392594
# Let's print all the unique ptrs now
393-
uptr1 = Foo(0x7fc4255028a0, data=foo1)
595+
uptr1 = Foo(0x7f9258c029e0, data=foo1)
394596
uptr2 = nullptr
395-
uptr3 = Foo(0x7fc4255028c0, data=foo2)
597+
uptr3 = Foo(0x7f9258c02a00, data=foo2)
396598
397599
# 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)
400602
</pre>

README.md.template

+5-3
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,11 @@ individually or as a whole.
1111
Let me know if this is useful to anyone or if there are any areas you want
1212
covered. This is (probably forever) a work in progress.
1313

14-
[How to use std::unique_ptr](unique_ptr/README.md)
14+
[How to use std::move](std_move/README.md)
1515

16-
[How to use std::shared_ptr](shared_ptr/README.md)
16+
[How to use std::unique_ptr](std_unique_ptr/README.md)
1717

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+
20+
[How to make your own wrapper around std::shared_ptr](std_shared_ptr_wrapper/README.md)
1921

RUNME

+7-4
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,13 @@
33
# This is a wrapper script to build all examples and update the documents
44
#
55
. common/common.sh
6-
for i in \
7-
unique_ptr \
8-
shared_ptr \
9-
shared_ptr_wrapper
6+
7+
SUBDIRS="std_move \
8+
std_unique_ptr \
9+
std_shared_ptr \
10+
std_shared_ptr_wrapper"
11+
12+
for i in $SUBDIRS
1013
do
1114
echo
1215
log $i:
File renamed without changes.
File renamed without changes.

0 commit comments

Comments
 (0)