Skip to content

Commit 29302f7

Browse files
committed
rerererererererererelaern unique_ptr
1 parent 3a1931c commit 29302f7

1 file changed

Lines changed: 102 additions & 54 deletions

File tree

‎cpp/unique_ptr.cpp‎

Lines changed: 102 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
44
Sample use case:
55
6-
- you want a dynamic array a dynmically allocated derived class
6+
- you want a dynamic array a dynamically allocated derived class
77
- thus you have to use pointers for polymorphism. Otherwise objects can have different sizes, and arrays can't be used.
88
- how to prevent memory leaks?
99
@@ -21,6 +21,7 @@ lifetime of objects, and release them immediately when they are done.
2121
class Base {
2222
public:
2323
static int count;
24+
Base() : Base(0) {}
2425
Base(int i) : i(i) {
2526
count++;
2627
}
@@ -29,60 +30,76 @@ class Base {
2930
}
3031
int i;
3132
};
32-
3333
int Base::count = 0;
3434

35-
/* No memory leaked. Destructors called. */
36-
void unique_ptr_test() {
37-
std::vector<std::unique_ptr<Base>> bases;
38-
for (int i = 0; i < 10; ++i) {
39-
bases.push_back(std::unique_ptr<Base>(new Base(i)));
40-
}
41-
}
42-
43-
/* Memory leak. Destructors never called. */
44-
void raw_ptr_test() {
45-
std::vector<Base *> bases;
46-
for (int i = 0; i < 10; ++i) {
47-
bases.push_back(new Base(i));
48-
}
49-
}
50-
51-
void manual_ptr_test() {
52-
std::vector<Base *> bases;
53-
for (int i = 0; i < 10; ++i) {
54-
bases.push_back(new Base(i));
55-
}
56-
for (auto base : bases) {
57-
delete base;
58-
}
59-
}
60-
6135
// Create unique pointer dynamically,
6236
// and transfers ownership to caller.
6337
std::unique_ptr<Base> return_unique_ptr() {
6438
return std::unique_ptr<Base>(new Base(1));
6539
}
6640

6741
int main() {
68-
/* Basic example. */
69-
assert(Base::count == 0);
70-
unique_ptr_test();
71-
assert(Base::count == 0);
72-
raw_ptr_test();
73-
assert(Base::count == 10);
74-
manual_ptr_test();
75-
assert(Base::count == 10);
76-
77-
// ERROR: Convert to raw pointer.
78-
// Not possible, the cast operator is not defined.
42+
/* Basic examples. */
43+
{
44+
// This is the problem case that we are trying to solve: memory leaks
45+
{
46+
{
47+
Base *base = new Base(1);
48+
assert(base->i == 1);
49+
assert(Base::count == 1);
50+
// You will forget to do this one day.
51+
//delete base;
52+
}
53+
// Then base is lost forever: it went out of scope.
54+
// And the memory has leaked.
55+
assert(Base::count == 1);
56+
// Hack up the count for future tests.
57+
Base::count = 0;
58+
}
59+
60+
// This is how unique_ptr solves it.
61+
{
62+
{
63+
std::unique_ptr<Base> base(new Base(1));
64+
assert(base->i == 1);
65+
assert(Base::count == 1);
66+
}
67+
// Destructor called automatically! No memory leaks!
68+
assert(Base::count == 0);
69+
}
70+
71+
}
72+
73+
/* More commonly, the pointers are inside another container.
74+
* When the parent goes out of scope, it just calls the
75+
* child constructors in the same way.
76+
*/
77+
{
78+
{
79+
std::vector<std::unique_ptr<Base>> bases;
80+
bases.push_back(std::unique_ptr<Base>(new Base(0)));
81+
bases.push_back(std::unique_ptr<Base>(new Base(1)));
82+
assert(bases[0]->i == 0);
83+
assert(bases[1]->i == 1);
84+
assert(Base::count == 2);
85+
}
86+
assert(Base::count == 0);
87+
}
88+
89+
// Convert to raw pointer.
7990
{
8091
std::unique_ptr<int> p(new int);
81-
//int *raw = p;
92+
int *raw;
93+
94+
// Not possible, the cast operator is not defined.
95+
//raw = p;
96+
97+
// But can be done explicitly with .get().
98+
raw = p.get();
8299
}
83100

84101
// Copy constructor is deleted.
85-
// This is what imposes uniqueness.
102+
// This imposes uniqueness of ownership.
86103
{
87104
std::unique_ptr<int> p(new int);
88105
// ERROR.
@@ -121,26 +138,42 @@ int main() {
121138
}
122139
}
123140

124-
/*
125-
# reset
141+
// # reset
142+
{
143+
/* With no argumetns, explicit destruction of pointer. Equivalent to `delete`.
144+
*
145+
* http://stackoverflow.com/questions/25609457/unique-ptr-explicit-delete
146+
*/
147+
{
148+
std::unique_ptr<Base> p(new Base(1));
149+
assert(Base::count == 1);
150+
p.reset();
151+
assert(Base::count == 0);
152+
}
126153

127-
Explicit destruction of pointer. Equivalent to `delete`.
154+
// There is no assignment operator, but reset with an argument
155+
// does what you would expect: release and replace.
156+
{
157+
std::unique_ptr<Base> p(new Base(1));
158+
assert(p->i == 1);
159+
assert(Base::count == 1);
128160

129-
http://stackoverflow.com/questions/25609457/unique-ptr-explicit-delete
130-
*/
131-
{
132-
Base::count = 0;
133-
std::unique_ptr<Base> p = std::unique_ptr<Base>(new Base(1));
134-
assert(Base::count == 1);
135-
p.reset();
161+
// Nope.
162+
// p = new Base(2);
163+
164+
// Yup.
165+
p.reset(new Base(2));
166+
assert(p->i == 2);
167+
assert(Base::count == 1);
168+
}
136169
assert(Base::count == 0);
137170
}
138171

139172
/*
140-
# unique_ptr function argments
173+
# unique_ptr function arguments
141174
142-
- transfering ownership TODO
143-
- use raw pointeres on the interface, and convert it to unique_ptr inside callee
175+
- transferring ownership TODO examples
176+
- use raw pointers on the interface, and convert it to unique_ptr inside callee
144177
- if you already have an unique_ptr, release() it
145178
- this allows you to not tie down to a specific smart pointer on the function interface
146179
- use unique_ptr on interface and move on caller.
@@ -181,4 +214,19 @@ int main() {
181214
assert(Base::count == 0);
182215
}
183216
#endif
217+
218+
/* Array inside unique_ptr
219+
* https://stackoverflow.com/questions/16711697/is-there-any-use-for-unique-ptr-with-array
220+
*/
221+
{
222+
size_t n = 2;
223+
Base::count = 0;
224+
{
225+
std::unique_ptr<Base[]> array(new Base[n]);
226+
array[0].i = 1;
227+
array[0].i = 1;
228+
assert(Base::count == 2);
229+
}
230+
assert(Base::count == 0);
231+
}
184232
}

0 commit comments

Comments
 (0)