Welcome to our deep dive into the world of C++ smart pointers! Today, we'll explore one of the most useful and powerful smart pointers: shared_ptr.
shared_ptr?A shared_ptr is a smart pointer introduced in C++11 that keeps track of the number of shared owners of an object. It automatically deletes the object when the last owner goes out of scope.
#include <memory>
// Declare a shared_ptr variable
std::shared_ptr<int> myInt;š” Pro Tip: The <memory> header provides all C++ smart pointers, including shared_ptr.
shared_ptrA shared_ptr can be constructed in several ways:
std::shared_ptr<int> myInt; // Default constructs an empty shared_ptrint data = 42;
std::shared_ptr<int> myInt(new int(data)); // Constructs a shared_ptr from a raw pointershared_ptrstd::shared_ptr<int> anotherInt(std::make_shared<int>(42)); // Constructs a shared_ptr from an existing shared_ptrš Note: make_shared is a C++14 utility function that creates a shared_ptr with a specified type and initial value.
shared_ptr and OwnershipEach shared_ptr manages its own copy of the controlled object. When a shared_ptr is destroyed, it decrements the reference count of the controlled object. If the reference count reaches zero, the object is deleted.
std::shared_ptr<int> p1(new int(42));
std::shared_ptr<int> p2(p1); // p1 and p2 share ownership of the intshared_ptr and AssignmentAssigning one shared_ptr to another does not transfer ownership. Instead, both pointers share the same controlled object:
std::shared_ptr<int> p1(new int(42));
std::shared_ptr<int> p2;
p2 = p1; // p1 and p2 now share the same intshared_ptr LifetimeIn order to ensure that the controlled object is destroyed when no longer needed, you should create a shared_ptr in the scope that matches the lifetime of the object. For example:
{
std::shared_ptr<int> p(new int(42));
// Use p here...
} // The shared_ptr is destroyed here, and the int is deleteduse_count() and unique() FunctionsThe use_count() function returns the number of shared owners of the controlled object, while the unique() function ensures that the controlled object has a reference count of one.
std::shared_ptr<int> p1(new int(42));
std::shared_ptr<int> p2(p1);
std::cout << p1.use_count() << std::endl; // Prints 2
p2.reset();
std::cout << p1.use_count() << std::endl; // Prints 1
p1.reset();
std::cout << p1.use_count() << std::endl; // Prints 0
p1.reset(new int(42));
p1.unique();
std::cout << p1.use_count() << std::endl; // Prints 1Let's create a simple shared counter example to better understand the power of shared_ptr:
#include <iostream>
#include <memory>
#include <atomic>
class SharedCounter {
public:
SharedCounter() : count_(new int(0)) {}
void increment() {
++*count_;
}
int get_count() const {
return *count_;
}
std::shared_ptr<SharedCounter> share() {
return shared_from_this();
}
private:
std::shared_ptr<SharedCounter> shared_from_this() {
return std::make_shared<SharedCounter>(*this);
}
std::shared_ptr<int> count_;
};
What happens when a `shared_ptr`'s controlled object is no longer needed?
int main() { SharedCounter counter;
std::shared_ptr<SharedCounter> p1(counter.share());
std::shared_ptr<SharedCounter> p2(counter.share());
p1->increment();
p2->increment();
std::cout << "Counter: " << counter.get_count() << std::endl; // Prints 2
// p1 and p2 share the same counter object
p1->increment();
p2->decrement();
std::cout << "Counter: " << counter.get_count() << std::endl; // Prints 3
return 0;
}
In this example, we've created a `SharedCounter` class that can be shared using a `shared_ptr`. The `increment()` and `decrement()` functions modify the shared counter, while the `get_count()` function retrieves the current count. The `share()` function returns a new `shared_ptr` to the `SharedCounter`.
That's it for today! You've learned the basics of C++ `shared_ptr`, including its construction, ownership management, and lifetime management. Next time, we'll delve into more advanced topics and examples using `shared_ptr`.
Happy coding! š¤š