```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
```