cpp-cheat/cpp/unique_ptr.cpp
2017-12-19 23:10:36 +00:00

185 lines
5.4 KiB
C++

/*
# unique_ptr
Sample use case:
- you want a dynamic array a dynmically allocated derived class
- thus you have to use pointers for polymorphism. Otherwise objects can have different sizes, and arrays can't be used.
- how to prevent memory leaks?
http://en.cppreference.com/w/cpp/memory/unique_ptr
unique_ptr may incur an extra dereferece cost, but it is usually well worth it.
In Java, everything can be though as a smart pointer (shared),
so using this is still more efficient than Java, since C++ can know the exact
lifetime of objects, and release them immediately when they are done.
*/
#include "common.hpp"
class Base {
public:
static int count;
Base(int i) : i(i) {
count++;
}
~Base() {
count--;
}
int i;
};
int Base::count = 0;
/* No memory leaked. Destructors called. */
void unique_ptr_test() {
std::vector<std::unique_ptr<Base>> bases;
for (int i = 0; i < 10; ++i) {
bases.push_back(std::unique_ptr<Base>(new Base(i)));
}
}
/* Memory leak. Destructors never called. */
void raw_ptr_test() {
std::vector<Base *> bases;
for (int i = 0; i < 10; ++i) {
bases.push_back(new Base(i));
}
}
void manual_ptr_test() {
std::vector<Base *> bases;
for (int i = 0; i < 10; ++i) {
bases.push_back(new Base(i));
}
for (auto base : bases) {
delete base;
}
}
// Create unique pointer dynamically,
// and transfers ownershipt to caller.
std::unique_ptr<Base> return_unique_ptr() {
return std::unique_ptr<Base>(new Base(1));
}
int main() {
/* Basic example. */
assert(Base::count == 0);
unique_ptr_test();
assert(Base::count == 0);
raw_ptr_test();
assert(Base::count == 10);
manual_ptr_test();
assert(Base::count == 10);
// ERROR: Convert to raw pointer.
// Not possible, the cast operator is not defined.
{
std::unique_ptr<int> p(new int);
//int *raw = p;
}
// Copy constructor is deleted.
// This is what imposes uniqueness.
{
std::unique_ptr<int> p(new int);
// ERROR.
//std::unique_ptr<int> p2(p);
// Consequence: for loops over vectors must use references &.
// http://stackoverflow.com/questions/20292682/iterating-through-vectorunique-ptrmytype-using-c11-for-loops
{
std::vector<std::unique_ptr<int>> is;
for (auto& i : is) {}
// ERROR.
//for (auto i : is) {}
}
// Must move glvalues.
// http://stackoverflow.com/questions/3283778/why-can-i-not-push-back-a-unique-ptr-into-a-vector
{
std::vector<std::unique_ptr<int>> is;
std::unique_ptr<int> i(new int(1));
is.push_back(std::move(i));
assert(*is.back() == 1);
// Without intermediate variable, we don't need to move, because it is an rvalue,
// and unique_ptr does have an move constructor.
is.push_back(std::unique_ptr<int>(new int(2)));
assert(*is.back() == 2);
// Analogously, must move containers instead of copy.
{
std::vector<std::unique_ptr<int>> is;
is.push_back(std::unique_ptr<int>(new int(1)));
//std::vector<std::unique_ptr<int>> is2(is);
std::vector<std::unique_ptr<int>> is2(std::move(is));
assert(*(is2.front()) == 1);
}
}
}
/*
# reset
Explicit destruction of pointer. Equivalent to `delete`.
http://stackoverflow.com/questions/25609457/unique-ptr-explicit-delete
*/
{
Base::count = 0;
std::unique_ptr<Base> p = std::unique_ptr<Base>(new Base(1));
assert(Base::count == 1);
p.reset();
assert(Base::count == 0);
}
/*
# unique_ptr function argments
- transfering ownership TODO
- use raw pointeres on the interface, and convert it to unique_ptr inside callee
- if you already have an unique_ptr, release() it
- this allows you to not tie down to a specific smart pointer on the function interface
- use unique_ptr on interface and move on caller.
- Advantage: unique_ptr on interface documents ownership transfer,
and prevents callee from passing non new pointer to it by mistake.
- TODO for not transfering ownership:
- `const & std::unique_ptr<T>`
- `get()`. Simple and efficient. But how to use it for containers like `vector<std::unique_ptr>`?
- `T&` on function, `*t` on caller. Looks good!
- http://stackoverflow.com/questions/8114276/how-do-i-pass-a-unique-ptr-argument-to-a-constructor-or-a-functionhttp://stackoverflow.com/questions/8114276/how-do-i-pass-a-unique-ptr-argument-to-a-constructor-or-a-function
- http://stackoverflow.com/questions/11277249/how-to-pass-stdunique-ptr-around
*/
{
}
// Return unique_ptr from function.
{
{
Base::count = 0;
auto base = return_unique_ptr();
assert(Base::count == 1);
assert(base->i == 1);
}
assert(Base::count == 0);
}
#if __cplusplus >= 201402L
// # make_unique
// Does new and puts it inside unique_ptr. Very convenient.
{
{
Base::count = 0;
auto base = std::make_unique<Base>(1);
assert(Base::count == 1);
assert(base->i == 1);
}
assert(Base::count == 0);
}
#endif
}