TDDD38 APiC++ Smartpointers 317

Smart pointers

A stores a pointer to another object. ¥ simplifies resource management Ð can prevent memory leaks if exceptions are thrown Ð can help ensure that resources are available as long as they are needed, and disposed of when no longer needed ¥ strong and weak references Ðastrong reference protects the referenced object from garbage collection Ð an object referenced only by weak references is considered unreachable and may be collected at any time ¥ C++ provide strong/ functionality for pointers Ð include Ð raw pointers are basically weak, but not true weak, since they don’t know when the referenced object becomes unreachable ¥ shared_ptr (strong) Ð smart pointer Ð the controlled resource is released when the use count (reference count) becomes 0 (unreachable) Ð supports copy semantics Ð shared_ptrs may be stored in containers ¥ weak_ptr (weak) Ð are like raw pointers, but know when they dangle (have expired) ¥ unique_ptr (strong) Ð supports moving instead of copying

File: Standard-Smart-Pointers-OH-en © 2014 Tommy Olsson, IDA, Linköpings universitet (2014-11-13) TDDD38 APiC++ Smartpointers 318 unique_ptr (1)

¥ retains sole ownership of an object through a pointer and destroys that object when the unique_ptr goes out of

template > class unique_ptr { … };

{//some block unique_ptr up1{ new Object }; // single object version

unique_ptr up2{ new Object[10] }; // array version

unique_ptr up3{ up1 }; // error Ð copy constructor is removed up1 = up3; // error Ð copy assignment is removed

unique_ptr up4{ std::move(up1) }; // ownership is transferred to up4 up1 = std::move(up4); // ownership is transferred to up1 }

¥ will dispose of the pointed-to object when destroyed itself Ð for example when leaving block scope Ð this is done with an associated deleter ¥ typical uses Ð providing exception safety by deleting dynamically allocated objects on both normal exit and exit through exception Ð transferring ownership of uniquely-owned objects with dynamic lifetime into functions (argument passing) Ð acquiring ownership of uniquely-owned objects with dynamic lifetime from functions (return value) Ð as the element type in move-aware containers, such as std::vector, which hold pointers to dynamically-allocated objects

File: Standard-Smart-Pointers-OH-en © 2014 Tommy Olsson, IDA, Linköpings universitet (2014-11-13) TDDD38 APiC++ Smartpointers 319 unique_ptr (2)

¥ constructors

unique_ptr up1; // default constructor Ð up1 owns nothing

unique_ptr up2{ nullptr }; // up2 owns nothing

unique_ptr up3{ new T }; // taking a pointer to an object Ð up3 owns the pointed to object

unique_ptr up4{ std::move(up3) }; // move constructor Ð ownership is transferred to up4

unique_ptr up5{ std::move(up4) }; // type converting constructor Ð ownership is transferred

Ð there is also a constructor for passing an auto_ptr smart pointer (C++98, deprecated in C++11) Ð there are also versions taking a deleter ¥ destructor Ð deletes the owned object ¥ assignment

up1 = std::move(up2); // move assignment Ð up2 becomes nullpointer

up1 = nullptr;//becomes as if default initialized

up1 = std::move(up5); // type converting assignment, also the deleter will be converted

File: Standard-Smart-Pointers-OH-en © 2014 Tommy Olsson, IDA, Linköpings universitet (2014-11-13) TDDD38 APiC++ Smartpointers 320 unique_ptr (3)

¥ observers

cout << *up << endl; // up must not be nullptr

cout << up->memfun() << endl; // up must not be nullptr

cout << up.get()->memfun() << endl; // get() returns the stored pointer

cout << up2[i] << endl; // array version elements are indexable

if (up) cout << up->memfun() << endl; // true if stored pointer is not nullptr

unique_ptr::deleter_type = get_deleter();

¥ modifiers

Object* p{ up.release() }; // sets up to nullptr

up.reset(p); // assigns new pointer, and then calls the deleter for the old pointer, if not nullptr

up1.swap(up2); // swaps pointers and deleters Ð also non-member version

up.reset(new int(2)) // assigns new pointer, and then calls the deleter for the old pointer

¥ comparisons Ð memory locations compared == != < <= > >=

File: Standard-Smart-Pointers-OH-en © 2014 Tommy Olsson, IDA, Linköpings universitet (2014-11-13) TDDD38 APiC++ Smartpointers 321

Standard library default deleters

¥ the single object deleter calls delete on the stored pointer Ð defined as primary template

template struct default_delete { constexpr default_delete() noexcept = default; template default_delete(const default_delete&) noexcept; void operator()(T* ptr) const { static_assert(sizeof(T) > 0, "can’t delete pointer to incomplete type"); delete ptr; } }; ¥ the array deleter is declared as an explicit specialization, which calls delete[] on the stored pointer

template struct default_delete { constexpr default_delete() noexcept = default; void operator()(T* ptr) const { static_assert(sizeof(T) > 0, "can’t delete pointer to incomplete type"); delete[] ptr; } template void operator()(U*) const = delete; };

File: Standard-Smart-Pointers-OH-en © 2014 Tommy Olsson, IDA, Linköpings universitet (2014-11-13) TDDD38 APiC++ Smartpointers 322

Shared-ownership pointers

¥ Implements semantics of shared ownership Ð stores a pointer, usually obtained via new Ð the last remaining owner of the pointer is responsible for destroying the object, or Ð otherwise releasing the resources associated with the stored pointer ¥ Two associated smart pointer types Ð shared_ptr Ð weak_ptr

File: Standard-Smart-Pointers-OH-en © 2014 Tommy Olsson, IDA, Linköpings universitet (2014-11-13) TDDD38 APiC++ Smartpointers 323 shared_ptr

¥ a shared_ptr object can take ownership of a pointer and manage the storage of that pointer

shared_ptr sp1{ new int{ 17 } }; // use count is to 1

Ð provide a limited garbage-collection facility ¥ shared_ptr objects can share ownership

shared_ptr sp2{ sp1 }; // use count is increased to 2

Ð the group of owners becomes responsible to delete the pointer when the last of them releases ownership ¥ shared_ptr objects can only share ownership by copying their values Ð two shared_ptr objects constructed from the same (non-shared) pointer will both own the pointer without sharing it

int* p = new int{ 4711 }; shared_ptr sp3{ p }; // use count is set to 1 shared_ptr sp4{ p }; // use count is set to 1

Ð if one such shared_ptr releases the pointer, deleting the managed object, leaves the other pointing to an invalid location ¥ ownership is released as soon as a shared pointer object is Ð destroyed Ð changed by an assignment Ð changed by a call to reset() if the use count is 1 when this happens, the managed pointer is deleted

File: Standard-Smart-Pointers-OH-en © 2014 Tommy Olsson, IDA, Linköpings universitet (2014-11-13) TDDD38 APiC++ Smartpointers 324

Inside shared_ptr

shared_ptr object

control block

(on heap) use count: 1 (strong count)

weak count: 0

(on heap)

¥ if several shared_ptr shares ownership of object they will also share the control block Ð use count (strong count) stores the number of strong references, i.e. shared_ptr, referring to object Ð weak count stores the number of weak references, i.e. weak_ptr, referring to object (none above)

File: Standard-Smart-Pointers-OH-en © 2014 Tommy Olsson, IDA, Linköpings universitet (2014-11-13) TDDD38 APiC++ Smartpointers 325 shared_ptr operations (1)

¥ constructors

shared_ptr sp1; // default constructor Ð sp1 is empty, use count of zero

shared_ptr sp2{ nullptr }; // from null pointer Ð same as default construction

shared_ptr sp3{ new int{17} }; // from pointer Ð sp3 owns the pointer, use count is set to 1

shared_ptr sp4{ sp3 }; // copy constructor Ð if sp3 is not empty ownership is shared

shared_ptr sp5{ std::move(sp3) }; // move constructor Ð sp3 becomes empty, as if default-constructed

shared_ptr sp6{ wp }; // copy from weak_ptr Ð bad_weak_ptr thrown if wp has expired

shared_ptr sp7{ up }; // move from unique_ptr, or auto_ptr

Ð aliasing constructor Ð example later ¥ destructor Ð if use count is greater than 1, the use count of the other objects sharing ownership is decreased by 1 Ð if use count is 1 (last referrer) the owned object is deleted Ð if use count is 0 (empty shared_ptr) the destructor has no side effects

File: Standard-Smart-Pointers-OH-en © 2014 Tommy Olsson, IDA, Linköpings universitet (2014-11-13) TDDD38 APiC++ Smartpointers 326 shared_ptr operations (2)

¥ assignment

sp1 = sp2; // copy assignment – add sp1 as a shared owner to sp2’s assets

sp1 = std::move(sp2); // move assignment Ð transfer ownership from sp2 to sp1, sp2 becomes empty

sp1 = std::move(up); // move from unique_ptr (or auto_ptr) Ð ownership is transferred, use count set to 1

sp1 = nullptr;//managed pointer is deleted, since use count is 1 Ð sp1 becomes empty

Ð a call to an assignment operator has the same effect as if the shared_ptr destructor was called before the value is changed, including the deletion of the managed object if use count was 1 (pointer was unique) Ð the value of a pointer cannot be assigned directly to a shared_ptr object

sp1 = std::make_shared(17); // make_shared returns shared_ptr (copy assignment)

sp1.reset(new int{ 17 });

Ð a call to reset() has the same effect as if the shared_ptr destructor was called before the value is changed

File: Standard-Smart-Pointers-OH-en © 2014 Tommy Olsson, IDA, Linköpings universitet (2014-11-13) TDDD38 APiC++ Smartpointers 327 shared_ptr operations (3)

¥ comparisons Ð memory locations comapred == != < <= > >=

sp1 == sp2 // returns sp1.get() == sp2.get()

sp1 < sp2 // returns sp1.get() < sp2.get()

sp == nullptr // returns !sp

¥ observers

p = sp.get(); // the stored pointer is returned (not the owned pointer if sp is aliased)

cout << *sp; // operator* returns a reference to the object pointed by the stored pointer

cout << sp->mem; // operator-> returns a pointer to the object pointed by the stored pointer

long n = sp.use_count(); // number of shared_ptr sharing ownership over the same pointer as sp

if (sp.unique())… // true if sp is sole referrer

if (sp)… // operator bool returns true if sp is not a null pointer

if (sp1.owner_before(sp2))… // …

File: Standard-Smart-Pointers-OH-en © 2014 Tommy Olsson, IDA, Linköpings universitet (2014-11-13) TDDD38 APiC++ Smartpointers 328 shared_ptr operations (4)

¥ modifiers

sp1.swap(sp2); // exchange content of sp1 and sp2 Ð transferring ownership, keeping use count swap(sp1, sp2); // non-member, ditto

sp1.reset(); // sp1 becomes empty (as if default-constructed) Ð if unique managed object is deleted sp1.reset(p); // sp1 acquire ownership of pointer p Ð if unique the previously managed object is deleted

sp2.reset(sp1.get()); // equivalent to shared_ptr(sp1.get()).swap(sp2)

cout << sp1 << ’\n’; // writes a system-specific textual representation of the stored pointer value

¥ shared_ptr creation

auto sp = std::make_shared>(17, "foobar");

¥ shared_ptr casts Ð static_pointer_cast(sp) returns a copy of sp with the stored pointer converted to U* (compatible by static_cast) Ð dynamic_pointer_cast(sp) returns a copy of a sp with the stored pointer converted to U* (compatible by dynamic_cast) Ð const_pointer_cast(sp) returns a copy of a sp with the stored pointer converted to U* (compatible by const_cast) ¥ there is more…

File: Standard-Smart-Pointers-OH-en © 2014 Tommy Olsson, IDA, Linköpings universitet (2014-11-13) TDDD38 APiC++ Smartpointers 329 weak_ptr

A weak_ptr object stores a weak reference to an object that is already managed by a shared_ptr.

shared_ptr sp{ new int{ 17 } };

weak_ptr wp{ sp };

¥ must be converted to shared_ptr in order to access the referred object

sp = wp.lock();

¥ models temporary ownership Ð when an object needs to be accessed only if it exists, and it may be deleted at any time by someone else, weak_ptr is used to track the object, and it is converted to std::shared_ptr to assume temporary ownership Ð if the original shared_ptr is destroyed at this time, the object’s lifetime is extended until the temporary shared_ptr is destroyed as well ¥ used to break cycles in data structures Ð dashed pointer is weak, the others are strong

Ð each object must contain both a shared_ptr and a weak_ptr Ð only one at a time is used

File: Standard-Smart-Pointers-OH-en © 2014 Tommy Olsson, IDA, Linköpings universitet (2014-11-13) TDDD38 APiC++ Smartpointers 330 weak_ptr operations (1)

¥ constructors

weak_ptr wp1; // default constructor

weak_ptr wp2{ wp1 }; // copy constructor Ð shares ownership with wp1, stores a copy of the pointer in wp1

weak_ptr wp3{ sp }; // copy from sp Ð shares ownership with sp, stores a copy of the pointer in sp

¥ destructor Ð destroys the weak_ptr object but has no effect on the object its stored pointer points to ¥ assignment

wp1 = wp2; // copy assignment

wp = sp; // shared_ptr objects can be assigned to weak_ptr objects directly, but

sp = wp.lock(); // assigning a weak_ptr object to a shared_ptr must be done using member lock()

File: Standard-Smart-Pointers-OH-en © 2014 Tommy Olsson, IDA, Linköpings universitet (2014-11-13) TDDD38 APiC++ Smartpointers 331 weak_ptr operations (2)

¥ modifiers

wp1.swap(wp2); // exchanges the contents, swapping their owning groups and any stored data

swap(wp1, wp2); // non-member swap, ditto

wp.reset(); // wp becomes empty, as if default constructed

¥ observers

long n = wp.use_count(); // number of shared_ptr objects that share ownership over the same pointer as wp

if (wp.expired())… // true if wp is empty or no more shared_ptr in the owner group wp belong to

sp = wp.lock(); // returns: expired() ? shared_ptr() : shared_ptr(*this)

if (wp1.owner_before(wp2))… // …

File: Standard-Smart-Pointers-OH-en © 2014 Tommy Olsson, IDA, Linköpings universitet (2014-11-13) TDDD38 APiC++ Smartpointers 332 shared_ptr and weak_ptr

shared_ptr p1{ new int{ 17 } }; // p1 takes ownership over the pointer

weak_ptr wp1(p1); // p1 owns the pointer

{ shared_ptr p2{ wp1.lock() }; // p1 and p2 now shares ownership

if (p2) // check to see if p2 is not a null pointer { …//do something with object referred by p2 } }//p2 is destroyed Ð p1 becomes sole owner to the pointer

p1.reset(); // memory is deleted Ð p1 is set to null pointer

shared_ptr p3{ wp1.lock() }; // wp1 is expired Ð lock() returns an empty shared_ptr

if (p3) { …//will not be executed }

File: Standard-Smart-Pointers-OH-en © 2014 Tommy Olsson, IDA, Linköpings universitet (2014-11-13) TDDD38 APiC++ Smartpointers 333

Inside shared_ptr and weak_ptr

shared_ptr

1

weak_ptr 1

¥ when a shared_ptr is first created, an additional control block is created on the heap Ð strong count is set to 1, weak count to 0 ¥ each time a copy of a shared_ptr is made, or created from a weak_ptr, strong count is incremented Ð when a shared_ptr is destroyed, strong count is decremented ¥ each time a weak_ptr is made, from a shared_ptr or a weak_ptr, weak count is incremented Ð when a weak_ptr is destroyed, weak count is decremented ¥ when the last shared_ptr is destroyed, the referred object is deleted Ðifweak count > 0 Ð the pointer in the control block is set to null, and strong count to 0 Ð remaining weak pointers have expired Ðifweak count is 0 Ð also control block is destroyed

File: Standard-Smart-Pointers-OH-en © 2014 Tommy Olsson, IDA, Linköpings universitet (2014-11-13) TDDD38 APiC++ Smartpointers 334 shared_ptr aliasing (1)

A shared_ptr object can share ownership over a pointer while at the same time point to another object Ð aliasing. ¥ the aliasing constructor takes a shared_ptr (r) and another pointer (p)

template shared_ptr(const shared_ptr& r, element_type* p);

Ð sp.get() == p Ð sp.use_count() == r.use_count() ¥ commonly used to point to member objects while owning the object they belong to (figure on next slide)

using Object_vector = vector;

shared_ptr store{ new Object_vector }; // use count == 1

store->push_back(Object()); // store a couple of objects store->push_back(Object());

shared_ptr sp1{ store, &store->at(0) }; // use count == 2

shared_ptr sp2{ store, &store->at(1) }; // use count == 3

File: Standard-Smart-Pointers-OH-en © 2014 Tommy Olsson, IDA, Linköpings universitet (2014-11-13) TDDD38 APiC++ Smartpointers 335 shared_ptr aliasing (2)

The code example on previous slide will result in the following:

store:

3

0

sp1:

sp2:

¥ store, sp1 and sp2 shares ownership of the pointer to the vector ¥ sp1 and sp2 points to a corresponding Object in the vector, but does not own that pointer

store.reset();

¥ use count is 3 when reset() is called Ð the managed vector is not deleted Ð use count is decreased to 2 ¥ when the last remaining owner is destroyed, reassigned, or reset, the vector will be destroyed, and then also the stored Objects

File: Standard-Smart-Pointers-OH-en © 2014 Tommy Olsson, IDA, Linköpings universitet (2014-11-13) TDDD38 APiC++ Smartpointers 336

More about strong and weak pointers and circular references in C++

See:

http://visualstudiomagazine.com/articles/2012/10/19/circular-references.aspx

File: Standard-Smart-Pointers-OH-en © 2014 Tommy Olsson, IDA, Linköpings universitet (2014-11-13)