|
1 | 1 | /* |
2 | 2 | # unique_ptr |
3 | 3 |
|
4 | | -TODO |
| 4 | +Sample use case: |
| 5 | +
|
| 6 | +- you want a dynamic array a dynmically allocated derived class |
| 7 | +- thus you have to use pointers for polymorphism. Otherwise objects can have different sizes, and arrays can't be used. |
| 8 | +- how to prevent memory leaks? |
| 9 | +
|
| 10 | +http://en.cppreference.com/w/cpp/memory/unique_ptr |
5 | 11 | */ |
6 | 12 |
|
7 | 13 | #include "common.hpp" |
8 | 14 |
|
| 15 | +class Base { |
| 16 | + public: |
| 17 | + static int count; |
| 18 | + Base(int i) : i(i) { |
| 19 | + count++; |
| 20 | + } |
| 21 | + ~Base() { |
| 22 | + count--; |
| 23 | + } |
| 24 | + int i; |
| 25 | +}; |
| 26 | + |
| 27 | +int Base::count = 0; |
| 28 | + |
| 29 | +/* No memory leaked. Destructors called. */ |
| 30 | +void unique_ptr_test() { |
| 31 | + std::vector<std::unique_ptr<Base>> bases; |
| 32 | + for (int i = 0; i < 10; ++i) { |
| 33 | + bases.push_back(std::unique_ptr<Base>(new Base(i))); |
| 34 | + } |
| 35 | +} |
| 36 | + |
| 37 | +/* Memory leak. Destructors never called. */ |
| 38 | +void raw_ptr_test() { |
| 39 | + std::vector<Base *> bases; |
| 40 | + for (int i = 0; i < 10; ++i) { |
| 41 | + bases.push_back(new Base(i)); |
| 42 | + } |
| 43 | +} |
| 44 | + |
| 45 | +void manual_ptr_test() { |
| 46 | + std::vector<Base *> bases; |
| 47 | + for (int i = 0; i < 10; ++i) { |
| 48 | + bases.push_back(new Base(i)); |
| 49 | + } |
| 50 | + for (auto base : bases) { |
| 51 | + delete base; |
| 52 | + } |
| 53 | +} |
| 54 | + |
9 | 55 | int main() { |
| 56 | + /* Basic example. */ |
| 57 | + assert(Base::count == 0); |
| 58 | + unique_ptr_test(); |
| 59 | + assert(Base::count == 0); |
| 60 | + raw_ptr_test(); |
| 61 | + assert(Base::count == 10); |
| 62 | + manual_ptr_test(); |
| 63 | + assert(Base::count == 10); |
| 64 | + |
| 65 | + // Copy constructor is deleted. |
| 66 | + // This is what imposes uniqueness. |
| 67 | + { |
| 68 | + std::unique_ptr<int> p(new int); |
| 69 | + // ERROR. |
| 70 | + //std::unique_ptr<int> p2(p); |
| 71 | + |
| 72 | + // Consequence: for loops over vectors must use references &. |
| 73 | + // http://stackoverflow.com/questions/20292682/iterating-through-vectorunique-ptrmytype-using-c11-for-loops |
| 74 | + { |
| 75 | + std::vector<std::unique_ptr<int>> is; |
| 76 | + for (auto& i : is) {} |
| 77 | + // ERROR. |
| 78 | + //for (auto i : is) {} |
| 79 | + } |
| 80 | + |
| 81 | + // Must move glvalues. |
| 82 | + // http://stackoverflow.com/questions/3283778/why-can-i-not-push-back-a-unique-ptr-into-a-vector |
| 83 | + { |
| 84 | + std::vector<std::unique_ptr<int>> is; |
| 85 | + std::unique_ptr<int> i(new int(1)); |
| 86 | + is.push_back(std::move(i)); |
| 87 | + assert(*is.back() == 1); |
| 88 | + |
| 89 | + // Without intermediate variable, we don't need to move, because it is an rvalue, |
| 90 | + // and unique_ptr does have an move constructor. |
| 91 | + is.push_back(std::unique_ptr<int>(new int(2))); |
| 92 | + assert(*is.back() == 2); |
| 93 | + |
| 94 | + // Analogously, must move containers instead of copy. |
| 95 | + { |
| 96 | + std::vector<std::unique_ptr<int>> is; |
| 97 | + is.push_back(std::unique_ptr<int>(new int(1))); |
| 98 | + //std::vector<std::unique_ptr<int>> is2(is); |
| 99 | + std::vector<std::unique_ptr<int>> is2(std::move(is)); |
| 100 | + assert(*(is2.front()) == 1); |
| 101 | + } |
| 102 | + } |
| 103 | + } |
10 | 104 | } |
0 commit comments