std::auto_ptr
std::unique_ptr
std::shared_ptr
Object that behaves as a pointer by overloading operator*()
and operator->()
// the simplest SmartPointer
template <typename T>
class SmartPointer
{
public:
T* operator->()
{
return m_Pointer;
}
T& operator* ()
{
return *m_Pointer;
}
private:
T* m_Pointer;
};
operator->()
Every time the compiler evaluates the ->
operator, until the right
side of the operator becomes a native pointer.
std::auto_ptr
std::unique_ptr
std::shared_ptr
std::weak_ptr
boost::intrusive_ptr
std::auto_ptr
A smart pointer that frees the memory upon destruction. In C++ 11 it was
removed from the language. It is fully replaced by std::unique_ptr
without any drawbacks
std::auto_ptr
template <typename T>
class auto_ptr {
// ...
T* operator->()
{
return m_Pointer;
}
T& operator* ()
{
return *m_Pointer;
}
// ...
T* m_Pointer;
};
template <typename T>
class auto_ptr {
// ...
auto_ptr()
: m_Pointer(nullptr)
{}
auto_ptr(T* pointer)
: m_Pointer(pointer)
{}
~auto_ptr()
{
delete m_Pointer;
}
// ...
T* m_Pointer;
};
template <typename T>
class auto_ptr {
// ...
// copy ctor changes the original!!!
auto_ptr(auto_ptr& o) : m_Pointer(o.m_Pointer) {
o.m_Pointer = nullptr;
}
// assignment changes the original!!!
auto_ptr& operator=(auto_ptr& rhs) {
if (this != &rhs) {
delete m_Pointer;
m_Pointer = rhs.m_Pointer;
rhs.m_Pointer = nullptr;
}
return *this;
}
// ...
T* m_Pointer;
};
Due to the lack of move semantics in C++ 98, std::auto_ptr
is forced
to transfer the ownership of the pointer during copy construction and
assignment. std::auto_ptr
was also copiable and assignable. You could
make a std::vector<std::auto_ptr>
, but it would be completely
unpredictable - the standard does not require certain copy/assignment
guarantees for containers.
std::unique_ptr
C++ 11 introduces std::unique_ptr
std::unique_ptr
template <typename T>
struct unique_ptr {
// ...
T* operator->()
{
return m_Pointer;
}
T& operator* ()
{
return *m_Pointer;
}
// ...
T* m_Pointer;
};
template <typename T>
struct unique_ptr {
// ...
unique_ptr()
: m_Pointer(nullptr)
{}
unique_ptr(T* pointer)
: m_Pointer(pointer)
{}
~unique_ptr()
{
delete m_Pointer;
}
// ...
T* m_Pointer;
};
template <typename T>
struct unique_ptr {
// ...
unique_ptr(const unique_ptr& o) = delete;
unique_ptr& operator= (const unique_ptr& rhs) = delete;
// ...
T* m_Pointer;
};
template <typename T>
struct unique_ptr {
// ...
unique_ptr(unique_ptr&& o)
: m_Pointer(o.m_Pointer)
{
o.m_Pointer = nullptr;
}
unique_ptr& operator=(unique_ptr&& rhs)
{
if (this != &rhs) {
delete m_Pointer;
m_Pointer = rhs.m_Pointer;
rhs.m_Pointer = nullptr;
}
return *this;
}
// ...
T* m_Pointer;
};
std::unique_ptr<T>
synopsisunique_ptr()
default constructorunique_ptr(Y*)
constructor - allows using pointers to
Derived
classes to be hold in std::unique_ptr<Base>
T& operator* ()
T* operator-> ()
const T& operator* () const
const T* operator-> () const
T* get()
- get the underlying pointerconst T* get() const
T* release()
- get the underlying pointer and release
ownership over it. Use it when you need to give up the ownership of the
resource
void reset(T* new_pointer = nullptr)
- reset the underlying
pointer to new_pointer
and get ownership over it. Deletes the
current pointer.
explicit operator bool()
allows usage such as
std::unique_ptr<T> p;
if (p) {
std::cout << *p << std::endl;
}
std::unique_ptr
has a second template parameter
template <typename T, typename Deleter = std::default_delete<T>>
class unique_ptr
{
~unique_ptr() {
auto deleter = get_deleter();
deleter(m_Pointer);
}
};
std::unique_ptr<Lifetime> a(new Lifetime[2]);
//std::cout << "Second: " << &a[1] << std::endl;
Will make a delete
to memory allocated with new []
std::unique_ptr<Lifetime[]> a(new Lifetime[2]);
std::cout << "Second: " << &a[1] << std::endl;
T& operator[](size_t index)
- allows array element access. Only
std::unique_ptr<T[]>
has this method.
struct ReleaseDelete {
template <typename T>
void operator()(T* pointer) {
pointer->Release();
}
};
// ...
std::unique_ptr<ID3D11Device, ReleaseDelete> device;
Will be back to unique_ptr
deleter when we get to:
std::unique_ptr
owns the resource it is pointing toUse this to express ownership relation between types
std::shared_ptr
std::shared_ptr
allows sharing of a resource. It is freed when the
last std::shared_ptr
instance pointing to the resource is destroyed.
std::shared_ptr
uses Reference
Counting.
A technique of storing the number of references, pointers, or handles to a resource.
A garbage collection algorithm that uses these reference counts to deallocate objects which are no longer referenced
std::shared_ptr<int> t(new int(42));// pointer 0xff00 : rc 1
std::shared_ptr<int> q = t; // pointer 0xff00 : rc 2
std::shared_ptr<int> p(new int(24));// pointer 0xfe00 : rc 1
t = p; // pointer 0xff00 : rc 1
// pointer 0xfe00 : rc 2
q = t; // pointer 0xff00 : rc 0, so it is deleted
// pointer 0xfe00 : rc 3
std::shared_ptr
The reference count and the resource need to be shared between all instances. So they have to be on the heap.
SharedPtr()
: m_RC(nullptr)
, m_Pointer(nullptr)
{}
SharedPtr(T* pointer)
: m_RC(new int(1))
, m_Pointer(pointer)
{
}
// ...
~SharedPtr()
{
RemoveReference();
}
// ...
int* m_RC;
T* m_Pointer;
void RemoveReference()
{
if (m_RC && --*m_RC == 0)
{
delete m_Pointer;
delete m_RC;
}
}
void AddReference()
{
++*m_RC;
}
SharedPtr(const SharedPtr& o)
: m_RC(o.m_RC)
, m_Pointer(o.m_Pointer)
{
AddReference();
}
SharedPtr& operator=(const SharedPtr& rhs)
{
if (this != &rhs)
{
RemoveReference();
m_RC = rhs.m_RC;
m_Pointer = rhs.m_Pointer;
AddReference();
}
return *this;
}
SharedPtr(SharedPtr&& o)
: m_RC(o.m_RC)
, m_Pointer(o.m_Pointer)
{
o.m_RC = nullptr;
o.m_Pointer = nullptr;
}
SharedPtr& operator=(SharedPtr&& rhs)
{
if (this != &rhs) {
RemoveReference();
m_RC = rhs.m_RC;
m_Pointer = rhs.m_Pointer;
rhs.m_RC = nullptr;
rhs.m_Pointer = nullptr;
}
return *this;
}
shared_ptr
is for sharingUse a shared_ptr
to express that the ownership of the resource is shared.
If you have a cycle in the references between resources in
std::shared_ptr
, these resources will never be freed.
std::weak_ptr
Weak references point to a resource, but do not prolong its lifetime.
std::weak_ptr<int> w;
{
std::shared_ptr<int> s = std::make_shared<int>(42);
w = s;
}
if (auto pointer = w.lock()) {
std::cout << *pointer << std::endl;
} else {
std::cout << "It's gone" << std::endl;
}
template <typename T>
class weak_ptr {
// ...
shared_ptr lock() {
if (<It is still alive>) {
return shared_ptr<T>(<for It>);
}
return shared_ptr();
}
};
shared_ptr
work?shared_ptr
?shared_ptr<T>
?std::shared_ptr<int> q;
{
std::shared_ptr<int> p = std::make_shared<int>(42);
q = p;
// p gets destroyed here, but it can't reach within q
// unless an expensive list of shared_ptr is stored
// somewhere
}
std::shared_ptr<int> p = std::make_shared<int>(42);
std::shared_ptr<int> q = std::make_shared<int>(42);
// oops, p and q have reference count 2, but actually point to
// different resources
shared_ptr
pricestd::make_shared
The reference count is stored in the resource (object) itself.
boost::intrusive_ptr
add a reference - calls intrusive_ptr_add_ref(T*)
release a reference - calls intrusive_ptr_release(T*)
class Renderer;
void intrusive_ptr_add_ref(Renderer* r) { ++r->refs; }
void intrusive_ptr_release(Renderer* r) { if (--r->refs == 0) { delete r; } }
intrusive_ptr
?shared_ptr
std???::intrusive_ptr
There is no std::intrusive_ptr
class, because std::make_shared
gives you
almost the same thing.
Using an intrusive_ptr
allows you to temporary give up
using smart pointers, go down to C
pointer and then go up
to an intrusive_ptr
std::enable_shared_from_this<T>
shared_ptr
from this
.std::weak_ptr
inside the object for the
object itselfclass Renderer : public std::enable_shared_from_this<Renderer>
{
};
std::shared_ptr<Renderer> get_the_shared_ptr(Renderer* r)
{
return r->shared_from_this();
}
new
std::make_shared<T>
is the best way to create a shared pointerC++
14 - std::make_unique<T>
- creates an unique pointerlink_ptr