- 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
newexpression
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
deleteoperator -
Refactor the solution to use
std::unique_ptr<> -
Use
std::make_unique()