mirror of
https://github.com/libretro/cpp-cheat.git
synced 2025-04-13 08:40:27 +00:00
185 lines
5.4 KiB
C++
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
|
|
}
|