- one object == one owner
- destructor destroys the object
- copying not allowed
- moving allowed
- can use custom deleter
- Old style approach vs modern approach
#include <iostream> // 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;
}
#include <memory> // modern approach
#include <iostream>
struct Msg {
int getValue() { return 42; }
};
std::unique_ptr<Msg> createMsg() {
return std::make_unique<Msg>();
}
int main() {
// unique ownership
auto msg = createMsg();
std::cout << msg->getValue();
}
- Copying is not allowed
- Moving is allowed
std::unique_ptr<MyData> source(void);
void sink(std::unique_ptr<MyData> 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);
}
std::unique_ptr<MyData> source(void);
void sink(std::unique_ptr<MyData> ptr);
void collections() {
std::vector<std::unique_ptr<MyData>> 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]));
}
#include <memory>
void legacyInterface(int*) {}
void deleteResource(int* p) { delete p; }
void referenceInterface(int&) {}
int main() {
auto ptr = std::make_unique<int>(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
#include <memory>
struct Msg {
Msg(int i) : value(i) {}
int value;
};
int main() {
auto ptr1 = std::unique_ptr<Msg>(new Msg{5});
auto ptr2 = std::make_unique<Msg>(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
struct MyData {};
void processPointer(MyData* md) {}
void processElement(MyData md) {}
using Array = std::unique_ptr<MyData[]>;
void use(void)
{
Array tab{new MyData[42]};
processPointer(tab.get());
processElement(tab[13]);
}
-
During destruction
-
std::unique_ptr<T>
callsdelete
-
std::unique_ptr<T[]>
callsdelete[]
-
-
std::unique_ptr<T[]>
has additionaloperator[]
for accessing array element -
Usually
std::vector<T>
is a better choice
- Compile and run ResourceD application
- Check memory leaks under valgrind
-
Fix memory leaks with a proper usage of
delete
operator -
Refactor the solution to use
std::unique_ptr<>
-
Use
std::make_unique()