std::auto_ptrstd::unique_ptrstd::shared_ptrObject 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_ptrstd::unique_ptrstd::shared_ptrstd::weak_ptrboost::intrusive_ptrstd::auto_ptrA 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_ptrtemplate <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_ptrC++ 11 introduces std::unique_ptr
std::unique_ptrtemplate <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* () constconst T* operator-> () constT* get() - get the underlying pointerconst T* get() constT* 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_ptrstd::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_ptrThe 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_ptrWeak 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_sharedThe reference count is stored in the resource (object) itself.
boost::intrusive_ptradd 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_ptrstd???::intrusive_ptrThere 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();
}
newstd::make_shared<T> is the best way to create a shared pointerC++14 - std::make_unique<T> - creates an unique pointerlink_ptr