# `std::unique_ptr<>` ___ ## `std::unique_ptr<>`
* one object == one owner * destructor destroys the object * copying not allowed * moving allowed * can use custom deleter * 0 cost class -> no impact on efficiency
unique pointers ___ ### `std::unique_ptr<>` usage * Old style approach vs modern approach
```cpp #include // old-style approach struct Msg { int getValue() { return 42; } }; Msg* createMsg() { return new Msg{}; } int main() { auto msg = createMsg(); std::cout << msg->getValue(); delete msg; } ```
```cpp #include // modern approach #include struct Msg { int getValue() { return 42; } }; std::unique_ptr createMsg() { return std::make_unique(); } int main() { // unique ownership auto msg = createMsg(); std::cout << msg->getValue(); } ```
___ ### `std::unique_ptr<>` usage * Copying is not allowed * Moving is allowed
```cpp std::unique_ptr source(void); void sink(std::unique_ptr ptr); void simpleUsage() { source(); sink(source()); auto ptr = source(); // sink(ptr); // compilation error sink(std::move(ptr)); auto p1 = source(); // auto p2 = p1; // compilation error auto p2 = std::move(p1); // p1 = p2; // compilation error p1 = std::move(p2); } ```
```cpp std::unique_ptr source(void); void sink(std::unique_ptr ptr); void collections() { std::vector> v; v.push_back(source()); auto tmp = source(); // v.push_back(tmp); // compilation error v.push_back(std::move(tmp)); // sink(v[0]); // compilation error sink(std::move(v[0])); } ```
___ #### `std::unique_ptr<>` problem with containers
What is wrong with this part of code? ```cpp std::unique_ptr source(void); void sink(std::unique_ptr ptr); void collections() { std::vector> v; v.push_back(source()); auto tmp = source(); v.push_back(std::move(tmp)); sink(std::move(v[0])); std::cout << *(v[0]) << '\n'; } ```
___ #### `std::unique_ptr<>` cooperation with raw pointers ```cpp #include void legacyInterface(int*) {} void deleteResource(int* p) { delete p; } void referenceInterface(int&) {} int main() { auto ptr = std::make_unique(5); legacyInterface(ptr.get()); deleteResource(ptr.release()); ptr.reset(new int{10}); referenceInterface(*ptr); ptr.reset(); // ptr is a nullptr return 0; } ``` * get() – returns a raw pointer without releasing the ownership * release() – returns a raw pointer and release the ownership * reset() – replaces the manager object * operator*() – dereferences pointer to the managed object ___ ### `std::make_unique()` ```cpp #include struct Msg { Msg(int i) : value(i) {} int value; }; int main() { auto ptr1 = std::unique_ptr(new Msg{5}); auto ptr2 = std::make_unique(5); // equivalent to above return 0; } ``` `std::make_unique()` is a factory function that produce `unique_ptrs` * added in C++14 for symmetrical operations on unique and shared pointers * avoids bare new expression ___ ### `std::unique_ptr` ```cpp struct MyData {}; void processPointer(MyData* md) {} void processElement(MyData md) {} using Array = std::unique_ptr; void use(void) { Array tab{new MyData[42]}; processPointer(tab.get()); processElement(tab[13]); } ``` * During destruction * std::unique_ptr<T> calls delete * std::unique_ptr<T[]> calls delete[] * std::unique_ptr<T[]> has additional operator[] for accessing array element * Usually std::vector<T> is a better choice ___ ## Exercise: Resource 1. Compile and run Resource application 2. Check memory leaks under valgrind 3. Fix memory leaks with a proper usage of delete operator 4. Refactor the solution to use std::unique_ptr<> 5. Use std::make_unique() ___ ## Exercise: Converter 1. Compile and run Converter application and check memory leaks under valgrind 2. Fix code using std::unique_ptr and std::make_unique 3. Find other issues and fix them (use good practise etc...) ___ ## Why virtual D'tor is so important (1)? ```C++ class Resource { public: explicit Resource(const std::string& str): str_(str) {} const std::string& str() const { return str_; } private: std::string str_; }; class Converter { public: Converter() { std::cout << "C'tor converter\n"; } virtual ~Converter() { std::cout << "D'tor converter\n"; } virtual void Convert(const std::unique_ptr& resource) const = 0; }; class CurlyBracketConverter : public Converter { public: CurlyBracketConverter() { std::cout << "C'tor CurlyBracketConverter\n"; } ~CurlyBracketConverter() override { std::cout << "D'tor CurlyBracketConverter\n"; } void Convert(const std::unique_ptr& resource) const override { std::cout << "{" << resource->str() << "}\n"; } }; class SquareBracketConverter : public Converter { public: SquareBracketConverter() { std::cout << "C'tor SquareBracketConverter\n"; } ~SquareBracketConverter() override { std::cout << "D'tor SquareBracketConverter\n"; } virtual void Convert(const std::unique_ptr& resource) const override{ std::cout << "[" << resource->str() << "]\n"; } }; class Printer { public: explicit Printer(std::unique_ptr converter): converter_(std::move(converter)) {} void Print(const std::unique_ptr& resource) const { converter_->Convert(resource); } private: std::unique_ptr converter_; }; int main() { auto resource = std::make_unique("Ala has a cat"); Printer printer(std::make_unique()); Printer printer2(std::make_unique()); return 0; } ``` ___ ## Why virtual D'tor is so important (2)? ```C++ C'tor converter C'tor SquareBracketConverter C'tor converter C'tor CurlyBracketConverter D'tor converter D'tor converter ``` Try to add virtual to your D'tor and check result ```C++ C'tor converter C'tor SquareBracketConverter C'tor converter C'tor CurlyBracketConverter D'tor CurlyBracketConverter D'tor converter D'tor SquareBracketConverter D'tor converter ``` ___ ## Custom Deleter * When there is a special way to delete object * Type of unique_ptr change!
```C++ class Foo { public: Foo() { std::cout << "Foo C'tor\n"; } void print() const { std::cout << "Foo!\n"; } private: // For some reason, we allow only this function to delete object friend void deleteMe(Foo* const foo); ~Foo() { std::cout << "Foo D'tor\n"; } }; void deleteMe(Foo* const foo) { std::cout << "Delete object Foo!\n"; delete foo; } int main() { // Can't use make unique, need to use unique_ptr C'tor // unique_ptr(pointer __p, const deleter_type& __d) noexcept std::unique_ptr ptr(new Foo, deleteMe); ptr->print(); return 0; } ```
Output ```C++ Foo C'tor Foo! Delete object Foo! Foo D'tor ```