I would like to use std::make_shared to create a void pointer. Since make_shared is supposed to be faster than shared_ptr(new T), and exception save I wonder if there is a library function to create a shared_ptr(new foo) in the make_shared way.
I would like to use std::make_shared to create a void pointer. Since make_shared is
Share
You can convert any
shared_ptr<foo>toshared_ptr<void>without the loss of efficiency associated withmake_shared:The conversion keeps the
fooand the reference count in the same memory allocation, even though you now refer to it via avoid*.Update
How does this work?
The general structure of a
std::shared_ptr<foo>is two pointers:p1points to a control block containing a reference count (actually two reference counts: one for strong owners and one for weak owners), a deleter, an allocator, and a pointer to the “dynamic” type of the object. The “dynamic” type is the type of the object that theshared_ptr<T>constructor saw, sayY(which may or may not be the same as aT).p2has typeT*where theTis the sameTas inshared_ptr<T>. Think of this as the “static” type of the stored object. When you dereference ashared_ptr<T>, it isp2that gets dereferenced. When you destruct ashared_ptr<T>, and if the reference count goes to zero, it is the pointer in the control block that aids in the destruction offoo.In the above diagram, both the control block and the
fooare dynamically allocated.p1is an owning pointer, and the pointer in the control block is an owning pointer.p2is a non-owning pointer.p2‘s only function is dereference (arrow operator,get(), etc.).When you use
make_shared<foo>(), the implementation has the opportunity to put thefooright in the control block, alongside of the reference counts and other data:The optimization here is that there is now only a single allocation: the control block which now embeds the
foo.When the above gets converted to a
shared_ptr<void>, all that happens is:I.e. The type of
p2changes fromfoo*tovoid*. That’s it. (besides incrementing/decrementing reference counts to account for a copy and destruction of a temporary — which can be elided by construction from an rvalue). When the reference count goes to zero, it is still the control block that destroys thefoo, found viap1.p2does not participate in the destruction operation.p1actually points to a generic base class of the control block. This base class is ignorant of the typefoostored in the derived control block. The derived control block is constructed inshared_ptr‘s constructor at the time the actual object typeYis known. But from then on theshared_ptrcan only communicate with the control block via acontrol_block_base*. So things like destruction happen via a virtual function call.The “move construction” of a
shared_ptr<void>from an rvalueshared_ptr<foo>in C++11 merely has to copy the two internal pointers, and does not have to manipulate the reference count. This is because the rvalueshared_ptr<foo>is about to go away anyway:This can be seen most plainly in the
shared_ptrconstructor source code:Before the converting construction the reference count is only 1. And after the converting construction the reference count is still 1, with the source pointing to nothing just prior to its destructor running. This, in a nutshell, is the joy of move semantics! 🙂