Smart pointers are objects in C++ that mimic the behavior of raw pointers but provide additional features such as automatic memory management. They help in avoiding common pitfalls associated with manual memory management, such as memory leaks and dangling pointers.
In this section, we’ll explore the creation and usage of unique_ptr objects in C++. unique_ptr provides exclusive ownership of dynamically allocated memory, ensuring that only one unique_ptr owns the resource at any given time.
#include
#include
void uniquePtrExample() {
// Creating a unique_ptr using std::make_unique
std::unique_ptr ptr = std::make_unique(10);
// Accessing the value stored in the dynamically allocated memory
std::cout << "Value: " << *ptr << std::endl;
} // Memory automatically deallocated when 'ptr' goes out of scope
int main() {
uniquePtrExample();
return 0;
}
// output //
Value: 10
<memory>
header to use unique_ptr.uniquePtrExample
function, we create a unique_ptr named ptr
using std::make_unique<int>(10)
. This dynamically allocates memory for an integer with value 10.ptr
and print the value stored in the dynamically allocated memory using *ptr
.uniquePtrExample
exits, the unique_ptr ptr
goes out of scope, and the dynamically allocated memory is automatically deallocated.Move semantics allow transferring ownership from one unique_ptr to another efficiently, ensuring that only one unique_ptr owns the resource at any given time.
#include
#include
void moveSemanticsExample() {
// Creating a unique_ptr
std::unique_ptr ptr1 = std::make_unique(20);
// Transferring ownership using move semantics
std::unique_ptr ptr2 = std::move(ptr1);
// Attempting to access the value stored in ptr1 (now nullptr)
// This will cause a runtime error
// std::cout << "Value: " << *ptr1 << std::endl;
}
int main() {
moveSemanticsExample();
return 0;
}
// output //
Runtime error: Segmentation fault (core dumped)
moveSemanticsExample
function, we create a unique_ptr ptr1
and allocate memory for an integer with value 20.ptr1
to ptr2
using std::move(ptr1)
.ptr1
after the move operation will result in a runtime error (segmentation fault) since ptr1
is now nullptr. This demonstrates that ownership of the memory has been transferred to ptr2
.In this section, we’ll explore the creation and usage of shared_ptr objects in C++. shared_ptr allows multiple pointers to share ownership of the same dynamically allocated memory, ensuring that the memory is deallocated only when all shared_ptr instances owning it are destroyed.
#include
#include
void sharedPtrExample() {
// Creating a shared_ptr using std::make_shared
std::shared_ptr ptr1 = std::make_shared(30);
// Creating another shared_ptr that shares ownership with ptr1
std::shared_ptr ptr2 = ptr1;
// Accessing the value stored in the dynamically allocated memory
std::cout << "Value: " << *ptr1 << std::endl;
std::cout << "Value: " << *ptr2 << std::endl;
} // Memory deallocated when both ptr1 and ptr2 go out of scope
int main() {
sharedPtrExample();
return 0;
}
// output //
Value: 30
Value: 30
sharedPtrExample
function, we create a shared_ptr ptr1
using std::make_shared<int>(30)
. This dynamically allocates memory for an integer with value 30.ptr2
and initialize it with ptr1
. Both ptr1
and ptr2
now share ownership of the same dynamically allocated memory.ptr1
and ptr2
to access the value stored in the dynamically allocated memory, and both pointers print the same value, demonstrating shared ownership.sharedPtrExample
exits, both ptr1
and ptr2
go out of scope, and the dynamically allocated memory is deallocated.shared_ptr uses reference counting to keep track of the number of shared_ptr instances owning the dynamically allocated memory. Memory is deallocated when the reference count reaches zero.
#include
#include
void referenceCountingExample() {
// Creating a shared_ptr
std::shared_ptr ptr1 = std::make_shared(40);
// Creating another shared_ptr that shares ownership with ptr1
std::shared_ptr ptr2 = ptr1;
// Checking the reference count
std::cout << "Reference count: " << ptr1.use_count() << std::endl;
} // Memory deallocated when both ptr1 and ptr2 go out of scope
int main() {
referenceCountingExample();
return 0;
}
// output //
Reference count: 2
referenceCountingExample
function, we create a shared_ptr ptr1
and dynamically allocate memory for an integer with value 40.ptr2
and initialize it with ptr1
, causing both pointers to share ownership of the same memory.use_count()
function to retrieve the reference count of ptr1
, which returns 2 since both ptr1
and ptr2
share ownership.referenceCountingExample
exits, both ptr1
and ptr2
go out of scope, and the dynamically allocated memory is deallocated.In this section, we’ll explore the creation and usage of weak_ptr objects in C++. weak_ptr allows observing an object managed by shared_ptr without affecting its ownership, preventing potential issues like circular dependencies and memory leaks.
#include
#include
void weakPtrExample() {
// Creating a shared_ptr
std::shared_ptr sharedPtr = std::make_shared(50);
// Creating a weak_ptr from shared_ptr
std::weak_ptr weakPtr = sharedPtr;
// Accessing the value stored in the dynamically allocated memory
if (auto ptr = weakPtr.lock()) {
std::cout << "Value: " << *ptr << std::endl;
} else {
std::cout << "Pointer expired" << std::endl;
}
}
int main() {
weakPtrExample();
return 0;
}
// output //
Value: 50
weakPtrExample
function, we first create a shared_ptr sharedPtr
and dynamically allocate memory for an integer with value 50.weakPtr
and initialize it with sharedPtr
. The weak_ptr observes the shared_ptr without affecting its ownership.lock()
function to obtain a shared_ptr from the weak_ptr. If the shared_ptr is still valid (i.e., the object has not been deleted), we dereference it and print the value stored in the dynamically allocated memory.sharedPtr
is still valid at this point, we successfully print the value 50.weak_ptr is often used to break strong reference cycles among shared_ptrs, preventing memory leaks and circular dependencies.
#include
#include
class Node {
public:
std::weak_ptr next;
// Other members...
};
void breakingCyclesExample() {
std::shared_ptr node1 = std::make_shared();
std::shared_ptr node2 = std::make_shared();
// Creating a circular reference
node1->next = node2;
node2->next = node1; // Causes a circular reference
// Breaking the cycle using weak_ptrs
node1->next = std::weak_ptr(node2);
node2->next = std::weak_ptr(node1);
}
int main() {
breakingCyclesExample();
return 0;
}
breakingCyclesExample
function, we create two shared_ptrs node1
and node2
, each pointing to a Node object.node1
‘s next
pointer to node2
and node2
‘s next
pointer to node1
.node1
nor node2
can be deallocated because they both hold a strong reference to each other.Smart pointers in C++, including unique_ptr, shared_ptr, and weak_ptr, provide powerful tools for managing dynamic memory and avoiding common pitfalls associated with manual memory management. By understanding their features and usage, developers can write safer, more robust code with fewer memory-related issues.Happy coding !❤️