

Inside STL: The different types of shared pointer control blocks
source link: https://devblogs.microsoft.com/oldnewthing/20230821-00/?p=108626
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Inside STL: The different types of shared pointer control blocks

Raymond Chen
We saw earlier that C++ standard library shared pointers use a control block to manage the object lifetime.
struct control_block { virtual void Dispose() = 0; virtual void Delete() = 0; std::atomic<unsigned long> shareds; std::atomic<unsigned long> refs; };
The control block has pure virtual methods, so it is up to derived classes to establish how to dispose and delete the control block.
If you ask a shared_ptr
to take responsibility for an already-constructed pointer, then you get this:
template<typename T> struct separate_control_block : control_block { virtual void Destroy() noexcept override { delete ptr; } virtual void Delete() noexcept override { delete this; } T* ptr; };
Added to the basic control block is a pointer to the managed object, which is delete
d when the last strong reference goes away.
If you use make_shared
or allocate_shared
, then the control block and the managed object are placed in the same allocation. In that case, the control block looks like this:
template<typename T> struct combined_control_block : control_block { virtual void Destroy() noexcept override { ptr()->~T(); } virtual void Delete() noexcept override { delete this; } T* ptr() { return reinterpret_cast<T*>(buffer); } // This buffer holds a "T" [[alignas(T)]] char buffer[sizeof(T)]; };
Added to the basic control block is a buffer suitable for holding a T
object. When the shared_ptr
is created, a T
is placement-constructed in that buffer, and when the last strong reference goes away (Destroy()
), it is destructed. Stephan T. Lavavej calls this the “We know where you live” optimization because the control block doesn’t need to store an explicit pointer to the buffer; it can derive it on the fly.
The reality is a little more complicated due to the need to store a deleter and possibly an allocator, but those are typically zero-length objects, so they get stored in a compressed pair with the other members.
In practice, when debugging, you don’t need to look past the reference counts in the control_block
. The thing you really care about in the shared_ptr
is the pointed-to object. If you ever look at the control block, it’s just to check whether there are any active strong references.
Bonus chatter: For C++20 make_shared<T[]>
, there’s another version of the control block that also has a count
member which specifies how many objects are in the storage.
Recommend
-
36
Introduction This is a write-up of the “behavioral analysis” of shared_ptr<T> reference count in GNU’s libstdc++. This smart pointer is used to share references to the same underlay...
-
10
Ruby has various ways of performing iteration—loops, blocks and enumerators. Most Ruby programmers are at least familiar with loops and blocks but Enumerator and Fiber often stay in the dark. In this edition of Ruby...
-
10
README.md A CRDT framework with a p...
-
6
Right now clippy lints instances of returning bare Err's via ? Err("foo")?; // clippy hates this return Err("foo".into()); // clippy's preference This perhaps...
-
9
Rust's Unsafe Pointer Types Need An Overhaul I think about unsafe pointers in Rust a lot. I literally wrote the book on unsafe Rust. And the book on
-
5
Beingessner: Rust's Unsafe Pointer Types Need An Overhaul [Posted March 21, 2022 by corbet] Aria Beingessner points out a set of pr...
-
10
Get the function pointer to run in a shared library I did not load directly advertisements My Linux application (A) links a...
-
7
Ruby has various ways of performing iteration—loops, blocks and enumerators. Most Ruby programmers are at least familiar with loops and blocks but Enumerator and Fiber often stay in the dark. In this edition of Ruby Magic...
-
2
What it means when you convert between different shared_ptrs
-
7
fix: Goto implementation to impls inside blocks #16812
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK