πSmart Pointer in C++ 2023
Smart pointers are a useful C++ feature to manage memory and avoid memory leaks.
unique_ptr: Represents exclusive ownership of a resource. The resource is destroyed when the unique_ptr goes out of scope.
shared_ptr: Represents shared ownership of a resource. The resource is destroyed when the last shared_ptr goes out of scope.
weak_ptr: Represents a "weak" reference to a resource managed by a shared_ptr. A weak_ptr does not prolong the lifetime of the resource.
unique_ptr:
Stores a unique pointer to an object
Object is deleted when unique_ptr goes out of scope or is reset()
Example:
Methods: get(), reset(), release(), operator->(), operator*()
shared_ptr:
Stores a shared pointer to an object
Object is deleted when all shared_ptrs are destroyed or reset
Example:
Methods: get(), reset(), use_count(), operator->(), operator*()
weak_ptr:
Stores a weak pointer to an object managed by shared_ptr
Does not increase the object's reference count
Example:
Methods: lock(), expired()
Hope this overview of the main smart pointer types in C++ is helpful!
All the methods of smart pointer
Here are the main methods for each smart pointer type in C++:
unique_ptr:
get() - Returns the underlying raw pointer
reset() - Resets the unique pointer, deletes the object
release() - Releases ownership of the object, returns the raw pointer
operator->() - Overloaded dereference operator
operator*() - Overloaded dereference operator
shared_ptr:
get() - Returns the underlying raw pointer
reset() - Resets the shared pointer, may delete the object if no other shared pointers exist
use_count() - Returns the number of shared pointers referring to the object
operator->() - Overloaded dereference operator
operator*() - Overloaded dereference operator
weak_ptr:
lock() - Attempts to lock the weak pointer, returning a shared pointer if the object still exists
expired() - Checks if the object managed by the weak pointer still exists
Some other common methods:
make_unique() - Creates a unique pointer
make_shared() - Creates a shared pointer
static_pointer_cast() - Casts between different pointer types
dynamic_pointer_cast() - Uses RTTI to cast between different pointer types
const_pointer_cast() - Casts away constness
alias() - Creates an alias shared pointer (shares ownership)
Hope this overview of the main methods for each smart pointer type in C++ is helpful!
get()
get() for unique_ptr:
get() for shared_ptr:
The get() method allows you to access the underlying raw pointer stored by the smart pointer. You can then dereference that pointer to access the object.
Keep in mind that after calling get(), the smart pointer still owns the object and will delete it when it goes out of scope. The raw pointer returned by get() is only valid as long as the smart pointer exists.
The get() method provides a way to interoperate with code that uses raw pointers.
Hope this helps explain get()! Let me know if you have any other questions.
reset()
Calling reset() on a unique_ptr will:
Delete the object it was pointing to (using delete)
Release ownership of that memory
Make the unique_ptr empty (no longer owning any memory)
This essentially "resets" the unique pointer, allowing you to call make_unique() again to allocate new memory.
For shared_ptr:
Calling reset() on a shared_ptr will:
Decrement the reference count
If the reference count reaches 0, the object will be deleted
Makes the shared_ptr empty, no longer owning any memory
So reset() allows you to essentially "reuse" the shared_ptr for a new allocation.
Hope this helps explain reset()! Let me know if you have any other questions.
release()
For unique_ptr:
up.release()
will:
Return the raw pointer owned by the unique_ptr
Release ownership of that memory
Leave the unique_ptr empty (no longer owning any memory)
This gives you the raw pointer so you can manually manage the memory. You are then responsible for deleting it.
For shared_ptr:
shared_ptr
does not have a release()
method since it uses reference counting.
Hope this helps explain release()
! Let me know if you have any other questions.
use_count()
use_count() for shared_ptr will return the number of shared_ptr objects that are managing the same memory.
For example:
Here:
p1
,p2
andp3
all point to the same memory allocationuse_count()
initially returns 3, as 3 shared pointers are managing that memoryWhen
p2
is reset(), the count decrements to 2
use_count()
allows you to determine how many shared pointers are currently managing an allocation. When the count reaches 0, the memory will be deallocated.
use_count()
is only available for shared_ptr
since it uses reference counting.
Hope this helps explain use_count()
! Let me know if you have any other questions.
lock()
For weak_ptr:
wp.lock()
will:
Attempt to "lock" the weak pointer by converting it to a shared pointer
Only succeeds if the memory is still allocated (the shared_ptr count is > 0)
Returns an empty shared_ptr if the memory has been deallocated
This allows you to check if the memory referenced by the weak pointer still exists, by locking it into a shared pointer.
lock()
returns a shared pointer (scoped into sp
in the example). You can then use that shared pointer to access the memory.
lock()
is only available for weak_ptr
since it does not own the memory itself.
Hope this helps explain lock()
! Let me know if you have any other questions.
expired()
For weak_ptr:
expired()
will simply return true if the memory referenced by the weak pointer has been deallocated. It will return false if the memory is still alive.
You can use expired()
to check if a weak pointer is still valid before attempting to lock()
it.
For example:
expired()
is only available for weak_ptr
since it does not own the memory itself. It must check the state of the corresponding shared_ptr
.
Hope this helps explain expired()
! Let me know if you have any other questions.
operator->() or operator*() - Overloaded dereference operator
The operator->() and operator*() methods are overloaded dereference operators for smart pointers. They allow you to access members and dereference the smart pointer similar to a raw pointer.
For example, with a unique_ptr:
Here we are able to call a method on the Foo object using either:
operator->() , which lets us use the -> access operator
operator*(), which lets us dereference the pointer using *
This allows smart pointers to be used in a similar way to raw pointers.
The overloaded dereference operators are implemented for:
unique_ptr
shared_ptr
weak_ptr
So in summary, operator->() and operator*() allow smart pointers to be used similarly to raw pointers, by overloading the pointer dereference syntax.
Hope this explanation helps! Let me know if you have any other questions regarding smart pointers.
make_unique() - Creates a unique pointer
make_unique() allows you to easily create a unique_ptr with one call, rather than manually allocating memory and constructing a unique_ptr.
For example:
Here std::make_unique<Foo>(10)
will:
Dynamically allocate memory for a Foo object
Call the Foo constructor with the value 10
Construct a unique_ptr managing that memory
All in one call.
Compared to:
make_unique() provides a cleaner syntax to create unique pointers.
It takes a template argument for the pointer type, and arguments to forward to the object's constructor.
Hope this helps explain make_unique()! Let me know if you have any other questions.
make_shared() - Creates a shared pointer
make_shared() works similarly to make_unique() but for shared_ptr instead.
It allows you to create a shared_ptr in one call, rather than manually allocating memory and constructing the shared_ptr.
For example:
This will:
Dynamically allocate memory for a Foo object
Call the Foo constructor with 10
Construct a shared_ptr managing that memory
All in one call.
Compared to:
make_shared() is generally preferred over manually allocating memory and constructing the shared_ptr because:
It allows the memory allocation and shared_ptr construction to be atomic
It may allocate the reference counter block alongside the object, reducing memory usage
Hope this helps explain make_shared()! Let me know if you have any other questions.
static_pointer_cast() - Casts between different pointer types
static_pointer_cast allows you to cast between different pointer types, similar to a static_cast.
For example:
Here we:
Create a unique_ptr to a Base class
It actually points to a Derived object
We cast it to a unique_ptr to Derived using static_pointer_cast
This performs a safe downcast from Base to Derived, since we know the actual type is Derived.
static_pointer_cast uses the compile time type information to perform the cast. It is similar to a static_cast.
Advantages over a dynamic_pointer_cast :
Faster - No RTTI is used
Safer - Compiler can check the cast is valid
static_pointer_cast allows you to cast between compatible smart pointer types where you know the actual type at compile time.
Hope this helps explain static_pointer_cast! Let me know if you have any other questions.
dynamic_pointer_cast() - Uses RTTI to cast between different pointer types
dynamic_pointer_cast uses RTTI (run-time type information) to perform a cast between compatible smart pointer types.
For example:
Here we:
Create a unique_ptr to a Base class
It actually points to a Derived object
We cast it to a unique_ptr to Derived using dynamic_pointer_cast
This performs a downcast from Base to Derived, using RTTI to determine the actual object type at run-time.
Advantages over static_pointer_cast:
Can cast to pointers of unrelated types, as long as a conversion exists
Will return a null pointer if the cast is not possible
dynamic_pointer_cast uses RTTI to determine if a cast is possible, and performs it at run-time.
It allows you to cast between smart pointer types when you don't know the exact types at compile time.
Hope this helps explain dynamic_pointer_cast! Let me know if you have any other questions.
const_pointer_cast() - Casts away constness
const_pointer_cast allows you to cast away the const qualifier from a smart pointer, effectively casting away constness.
For example:
Here we:
Create a shared pointer to a const int
Cast it to a shared pointer to a non-const int using const_pointer_cast
We are then able to modify the value, since we cast away const
const_pointer_cast effectively performs a const_cast on the underlying pointer.
It should be used with caution, as casting away constness can easily lead to undefined behavior.
const_pointer_cast allows you to cast smart pointers while casting away the const qualifier, but it is not recommended in most cases. Prefer avoiding the need for such casts where possible.
Hope this helps explain const_pointer_cast! Let me know if you have any other questions.
alias() - Creates an alias shared pointer (shares ownership) explain one by one
alias() for shared_ptr allows you to create an alias shared pointer that refers to the same object and shares ownership.
For example:
Here:
We create a shared pointer
p1
pointing to an integerWe call
alias()
onp1
to create an alias shared pointerp2
After this, both p1
and p2
:
Point to the same integer object
Share ownership of that object
Have the same reference count
When either p1
or p2
is destroyed or reset, the other will still be valid since they share ownership.
alias()
allows you to easily create a second shared pointer that refers to the same object, without copying the reference count.
The new alias shared pointer's reference count is tied to the original shared pointer.
Hope this helps explain alias()! Let me know if you have any other questions.
Last updated