Middle
How does the reference counter work in std::shared_ptr?
sobes.tech AI
Answer from AI
The reference counter in std::shared_ptr is stored in a separate object — the control block.
The control block is created:
- When the first
std::shared_ptris created from a pointer. - When creating a
std::shared_ptrwith a custom deleter or allocator. - When using
std::make_sharedorstd::allocate_shared.
This control block contains at least two counters:
- Strong reference count: Increases when creating or copying a
std::shared_ptr. Decreases when destroying astd::shared_ptr. When this counter reaches zero, the managed object is released. - Weak reference count: Increases when creating a
std::weak_ptrfrom astd::shared_ptr. Decreases when destroying astd::weak_ptr. The control block is released when both counters — strong and weak — reach zero.
Using std::make_shared is preferable over direct creation with new, as it can allocate the object and control block in a single memory block, improving performance and reducing fragmentation.
Counters are atomic, making std::shared_ptr safe for use in multithreaded scenarios, although access to the managed object itself is not synchronized by default.
Example:
#include <iostream>
#include <memory>
struct MyObject {
int data;
MyObject(int d) : data(d) {
std::cout << "MyObject(" << data << ") created\n";
}
~MyObject() {
std::cout << "MyObject(" << data << ") destroyed\n";
}
};
int main() {
std::shared_ptr<MyObject> ptr1 = std::make_shared<MyObject>(10);
// strong count: 1, weak count: 0
{
std::shared_ptr<MyObject> ptr2 = ptr1;
// strong count: 2, weak count: 0
std::weak_ptr<MyObject> weak_ptr = ptr1;
// strong count: 2, weak count: 1
if (auto locked_ptr = weak_ptr.lock()) {
// strong count: 3, weak count: 1 (inside lock, temporarily)
std::cout << "Object data: " << locked_ptr->data << std::endl;
}
// strong count: 2, weak count: 1
} // ptr2 goes out of scope
// strong count: 1, weak count: 1
// The object is not yet destroyed, as strong count == 1
{
std::weak_ptr<MyObject> weak_ptr2 = ptr1;
// strong count: 1, weak count: 2
} // weak_ptr2 goes out of scope
// strong count: 1, weak count: 1
} // ptr1 goes out of scope
// strong count: 0, weak count: 1
// The object is destroyed as the strong count reached zero
// The control block remains until the weak count reaches zero