Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<p>You can convert any <code>shared_ptr&lt;foo&gt;</code> to <code>shared_ptr&lt;void&gt;</code> without the loss of efficiency associated with <code>make_shared</code>:</p> <pre><code>#include &lt;memory&gt; struct foo {}; int main() { std::shared_ptr&lt;void&gt; p = std::make_shared&lt;foo&gt;(); } </code></pre> <p>The conversion keeps the <code>foo</code> and the reference count in the same memory allocation, even though you now refer to it via a <code>void*</code>.</p> <p><strong>Update</strong></p> <p>How does this work?</p> <p>The general structure of a <code>std::shared_ptr&lt;foo&gt;</code> is two pointers:</p> <pre><code> +------&gt; foo | ^ p1 ---------&gt; (refcount, +) | p2 --- foo* -----------------------+ </code></pre> <p><code>p1</code> points 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 the <code>shared_ptr&lt;T&gt;</code> constructor saw, say <code>Y</code> (which may or may not be the same as a <code>T</code>).</p> <p><code>p2</code> has type <code>T*</code> where the <code>T</code> is the same <code>T</code> as in <code>shared_ptr&lt;T&gt;</code>. Think of this as the "static" type of the stored object. When you dereference a <code>shared_ptr&lt;T&gt;</code>, it is <code>p2</code> that gets dereferenced. When you destruct a <code>shared_ptr&lt;T&gt;</code>, and if the reference count goes to zero, it is the pointer in the control block that aids in the destruction of <code>foo</code>.</p> <p>In the above diagram, both the control block and the <code>foo</code> are dynamically allocated. <code>p1</code> is an owning pointer, and the pointer in the control block is an owning pointer. <code>p2</code> is a non-owning pointer. <code>p2</code>'s <strong>only</strong> function is dereference (arrow operator, <code>get()</code>, etc.).</p> <p>When you use <code>make_shared&lt;foo&gt;()</code>, the implementation has the opportunity to put the <code>foo</code> right in the control block, alongside of the reference counts and other data:</p> <pre><code>p1 ---------&gt; (refcount, foo) p2 --- foo* --------------^ </code></pre> <p>The optimization here is that there is now only a single allocation: the control block which now embeds the <code>foo</code>.</p> <p>When the above gets converted to a <code>shared_ptr&lt;void&gt;</code>, all that happens is:</p> <pre><code>p1 ---------&gt; (refcount, foo) p2 --- void* -------------^ </code></pre> <p>I.e. The type of <code>p2</code> changes from <code>foo*</code> to <code>void*</code>. 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 the <code>foo</code>, found via <code>p1</code>. <code>p2</code> does not participate in the destruction operation.</p> <p><code>p1</code> actually points to a generic base class of the control block. This base class is ignorant of the type <code>foo</code> stored in the derived control block. The derived control block is constructed in <code>shared_ptr</code>'s constructor at the time the actual object type <code>Y</code> is known. But from then on the <code>shared_ptr</code> can only communicate with the control block via a <code>control_block_base*</code>. So things like destruction happen via a virtual function call.</p> <p>The "move construction" of a <code>shared_ptr&lt;void&gt;</code> from an rvalue <code>shared_ptr&lt;foo&gt;</code> in C++11 merely has to copy the two internal pointers, and does not have to manipulate the reference count. This is because the rvalue <code>shared_ptr&lt;foo&gt;</code> is about to go away anyway:</p> <pre><code>// shared_ptr&lt;foo&gt; constructed and destructed within this statement std::shared_ptr&lt;void&gt; p = std::make_shared&lt;foo&gt;(); </code></pre> <p>This can be seen most plainly in the <code>shared_ptr</code> constructor source code:</p> <pre><code>template&lt;class _Tp&gt; template&lt;class _Yp&gt; inline _LIBCPP_INLINE_VISIBILITY shared_ptr&lt;_Tp&gt;::shared_ptr(shared_ptr&lt;_Yp&gt;&amp;&amp; __r, typename enable_if&lt;is_convertible&lt;_Yp*, _Tp*&gt;::value, __nat&gt;::type) _NOEXCEPT : __ptr_(__r.__ptr_), __cntrl_(__r.__cntrl_) { __r.__ptr_ = 0; __r.__cntrl_ = 0; } </code></pre> <p>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! :-)</p>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload