Further edits, predestructor technique removed.

[SVN r17374]
This commit is contained in:
Peter Dimov
2003-02-13 18:35:13 +00:00
parent f4dce1cb88
commit d7c841484a
2 changed files with 30 additions and 54 deletions

View File

@ -32,7 +32,6 @@
object</A><br> object</A><br>
<A href="#extra_data">Associating arbitrary data with heterogeneous <code>shared_ptr</code> <A href="#extra_data">Associating arbitrary data with heterogeneous <code>shared_ptr</code>
instances</A><br> instances</A><br>
<A href="#pre_destructor">Post-constructors and pre-destructors</A><br>
<A href="#as_lock">Using <code>shared_ptr</code> as a CopyConstructible mutex lock</A><br> <A href="#as_lock">Using <code>shared_ptr</code> as a CopyConstructible mutex lock</A><br>
<A href="#wrapper">Using <code>shared_ptr</code> to wrap member function calls</A><br> <A href="#wrapper">Using <code>shared_ptr</code> to wrap member function calls</A><br>
<A href="#delayed">Delayed deallocation</A><br> <A href="#delayed">Delayed deallocation</A><br>
@ -360,7 +359,7 @@ shared_ptr&lt;X&gt; make_shared_from_another(another_ptr&lt;X&gt; qx)
instance. Example:</p> instance. Example:</p>
<pre>void f(X * p) <pre>void f(X * p)
{ {
shared_ptr&lt;X&gt; px(<i>???</i>); shared_ptr&lt;X&gt; px(<i>???</I>);
} }
</pre> </pre>
<p>Inside <code>f</code>, we'd like to create a <code>shared_ptr</code> to <code>*p</code>.</p> <p>Inside <code>f</code>, we'd like to create a <code>shared_ptr</code> to <code>*p</code>.</p>
@ -390,7 +389,7 @@ public:
X() X()
{ {
shared_ptr&lt;X&gt; this_(<i>???</i>); shared_ptr&lt;X&gt; this_(<i>???</I>);
} }
}; };
</pre> </pre>
@ -401,8 +400,7 @@ public:
<P>but at construction time, <code>px</code> does not exist yet, and it is <P>but at construction time, <code>px</code> does not exist yet, and it is
impossible to create another <code>shared_ptr</code> instance that shares impossible to create another <code>shared_ptr</code> instance that shares
ownership with it.</P> ownership with it.</P>
<P> <P>Depending on context, if the inner <code>shared_ptr</code> <code>this_</code> doesn't
Depending on context, if the inner <code>shared_ptr</code> <code>this_</code> doesn't
need to keep the object alive, use a <code>null_deleter</code> as explained <A href="#static"> need to keep the object alive, use a <code>null_deleter</code> as explained <A href="#static">
here</A> and <A href="#weak_without_shared">here</A>. If <code>X</code> is here</A> and <A href="#weak_without_shared">here</A>. If <code>X</code> is
supposed to always live on the heap, and be managed by a <code>shared_ptr</code>, supposed to always live on the heap, and be managed by a <code>shared_ptr</code>,
@ -463,7 +461,7 @@ public:
virtual shared_ptr&lt;X&gt; getX() virtual shared_ptr&lt;X&gt; getX()
{ {
shared_ptr&lt;X&gt; px(<i>???</i>); shared_ptr&lt;X&gt; px(<i>???</I>);
return px; return px;
} }
}; };
@ -500,8 +498,7 @@ public:
</pre> </pre>
<p>The library now includes a helper class template <code>enable_shared_from_this</code> <p>The library now includes a helper class template <code>enable_shared_from_this</code>
that can be used to encapsulate the solution:</p> that can be used to encapsulate the solution:</p>
<pre> <pre>class impl: public X, public Y, public enable_shared_from_this&lt;impl&gt;
class impl: public X, public Y, public enable_shared_from_this&lt;impl&gt;
{ {
public: public:
@ -536,8 +533,8 @@ handle createProcess()
} }
</pre> </pre>
<h2><A name="on_block_exit">Using <code>shared_ptr</code> to execute code on block exit</A></h2> <h2><A name="on_block_exit">Using <code>shared_ptr</code> to execute code on block exit</A></h2>
<p>It is possible to use <code>shared_ptr&lt;void&gt;</code> to automatically <p><code>shared_ptr&lt;void&gt;</code> can automatically execute cleanup code when
execute cleanup code when control leaves a scope.</p> control leaves a scope.</p>
<UL> <UL>
<LI> <LI>
Executing <code>f(p)</code>, where <code>p</code> is a pointer:</LI></UL> Executing <code>f(p)</code>, where <code>p</code> is a pointer:</LI></UL>
@ -546,16 +543,16 @@ handle createProcess()
<UL> <UL>
<LI> <LI>
Executing arbitrary code: <code>f(x, y)</code>:</LI></UL> Executing arbitrary code: <code>f(x, y)</code>:</LI></UL>
<pre> shared_ptr&lt;void&gt; guard(static_cast&lt;void*&gt;(0), <A href="../bind/bind.html">bind</A>(f, x, y)); <pre> shared_ptr&lt;void&gt; guard(static_cast&lt;void*&gt;(0), <A href="../bind/bind.html" >bind</A>(f, x, y));
</pre> </pre>
<P>For a more thorough treatment, see the article "Simplify Your Exception-Safe <P>For a more thorough treatment, see the article "Simplify Your Exception-Safe
Code" by Andrei Alexandrescu and Petru Marginean, available online at <A href="http://www.cuj.com/experts/1812/alexandr.htm?topic=experts"> Code" by Andrei Alexandrescu and Petru Marginean, available online at <A href="http://www.cuj.com/experts/1812/alexandr.htm?topic=experts">
http://www.cuj.com/experts/1812/alexandr.htm?topic=experts</A>.</P> http://www.cuj.com/experts/1812/alexandr.htm?topic=experts</A>.</P>
<h2><A name="pvoid">Using <code>shared_ptr&lt;void&gt;</code> to hold an arbitrary <h2><A name="pvoid">Using <code>shared_ptr&lt;void&gt;</code> to hold an arbitrary
object</A></h2> object</A></h2>
<p>It is possible to use <code>shared_ptr&lt;void&gt;</code> as a generic object <p><code>shared_ptr&lt;void&gt;</code> can act as a generic object pointer similar
pointer similar to <code>void*</code>. When a <code>shared_ptr&lt;void&gt;</code> to <code>void*</code>. When a <code>shared_ptr&lt;void&gt;</code> instance
instance constructed as:</p> constructed as:</p>
<pre> shared_ptr&lt;void&gt; pv(new X); <pre> shared_ptr&lt;void&gt; pv(new X);
</pre> </pre>
<p>is destroyed, it will correctly dispose of the <code>X</code> object by <p>is destroyed, it will correctly dispose of the <code>X</code> object by
@ -566,7 +563,10 @@ handle createProcess()
static_pointer_cast</A></code>.</p> static_pointer_cast</A></code>.</p>
<h2><A name="extra_data">Associating arbitrary data with heterogeneous <code>shared_ptr</code> <h2><A name="extra_data">Associating arbitrary data with heterogeneous <code>shared_ptr</code>
instances</A></h2> instances</A></h2>
<p>[...]</p> <p><code>shared_ptr</code> and <code>weak_ptr</code> support <code>operator&lt;</code>
comparisons required by standard associative containers such as <code>std::map</code>.
This can be used to non-intrusively associate arbitrary data with objects
managed by <code>shared_ptr</code>:</p>
<pre>typedef int Data; <pre>typedef int Data;
std::map&lt; shared_ptr&lt;void&gt;, Data &gt; userData; std::map&lt; shared_ptr&lt;void&gt;, Data &gt; userData;
@ -577,38 +577,11 @@ shared_ptr&lt;int&gt; pi(new int(3));
userData[px] = 42; userData[px] = 42;
userData[pi] = 91; userData[pi] = 91;
</pre>
<h2><A name="pre_destructor">Post-constructors and pre-destructors</A></h2>
<p>[...]</p>
<pre>class X
{
public:
X();
virtual void postconstructor();
virtual void predestructor() throw();
~X() throw();
struct deleter
{
void operator()(X * p)
{
p-&gt;predestructor();
delete p;
}
}
static shared_ptr&lt;X&gt; create()
{
shared_ptr&lt;X&gt; px(new X, X::deleter());
px-&gt;postconstructor(); // can throw
return px;
}
};
</pre> </pre>
<h2><A name="as_lock">Using <code>shared_ptr</code> as a CopyConstructible mutex lock</A></h2> <h2><A name="as_lock">Using <code>shared_ptr</code> as a CopyConstructible mutex lock</A></h2>
<p>[Sometimes it's necessary to return a mutex lock from a function. A noncopyable <p>Sometimes it's necessary to return a mutex lock from a function, and a
lock cannot be used.]</p> noncopyable lock cannot be returned by value. It is possible to use <code>shared_ptr</code>
as a mutex lock:</p>
<pre>class mutex <pre>class mutex
{ {
public: public:
@ -623,7 +596,8 @@ shared_ptr&lt;mutex&gt; lock(mutex &amp; m)
return shared_ptr&lt;mutex&gt;(&amp;m, mem_fn(&amp;mutex::unlock)); return shared_ptr&lt;mutex&gt;(&amp;m, mem_fn(&amp;mutex::unlock));
} }
</pre> </pre>
<p>[Or to encapsulate it in a dedicated class:]</p> <p>Better yet, the <code>shared_ptr</code> instance acting as a lock can be
encapsulated in a dedicated <code>shared_lock</code> class:</p>
<pre>class shared_lock <pre>class shared_lock
{ {
private: private:
@ -635,11 +609,11 @@ public:
template&lt;class Mutex&gt; explicit shared_lock(Mutex &amp; m): pv((m.lock(), &amp;m), mem_fn(&amp;Mutex::unlock)) {} template&lt;class Mutex&gt; explicit shared_lock(Mutex &amp; m): pv((m.lock(), &amp;m), mem_fn(&amp;Mutex::unlock)) {}
}; };
</pre> </pre>
<p>[Usage:]</p> <p><code>shared_lock</code> can now be used as:</p>
<pre> shared_lock lock(m); <pre> shared_lock lock(m);
</pre> </pre>
<p>[Note that <code>shared_lock</code> is not templated on the mutex type, thanks <p>Note that <code>shared_lock</code> is not templated on the mutex type, thanks to <code>
to <code>shared_ptr&lt;void&gt;</code>'s ability to hide type information.]</p> shared_ptr&lt;void&gt;</code>'s ability to hide type information.</p>
<h2><A name="wrapper">Using <code>shared_ptr</code> to wrap member function calls</A></h2> <h2><A name="wrapper">Using <code>shared_ptr</code> to wrap member function calls</A></h2>
<p>[http://www.research.att.com/~bs/wrapper.pdf]</p> <p>[http://www.research.att.com/~bs/wrapper.pdf]</p>
<pre>template&lt;class T&gt; class pointer <pre>template&lt;class T&gt; class pointer
@ -686,8 +660,8 @@ int main()
} }
</pre> </pre>
<h2><A name="delayed">Delayed deallocation</A></h2> <h2><A name="delayed">Delayed deallocation</A></h2>
<p>[In some situations, a single <code>px.reset()</code> can trigger an expensive <p>In some situations, a single <code>px.reset()</code> can trigger an expensive
deallocation in a performance-critical region.]</p> deallocation in a performance-critical region:</p>
<pre>class X; // ~X is expensive <pre>class X; // ~X is expensive
class Y class Y
@ -702,7 +676,9 @@ public:
} }
}; };
</pre> </pre>
<p>[Solution 1]</p> <p>The solution is to postpone the potential deallocation by moving <code>px</code>
to a dedicated free list that can be periodically emptied when performance and
response times are not an issue:</p>
<pre>vector&lt; shared_ptr&lt;void&gt; &gt; free_list; <pre>vector&lt; shared_ptr&lt;void&gt; &gt; free_list;
class Y class Y
@ -720,7 +696,8 @@ public:
// periodically invoke free_list.clear() when convenient // periodically invoke free_list.clear() when convenient
</pre> </pre>
<p>[Solution 2, as above, but use a delayed deleter]</p> <p>Another variation is to move the free list logic to the construction point by
using a delayed deleter:</p>
<pre>struct delayed_deleter <pre>struct delayed_deleter
{ {
template&lt;class T&gt; void operator()(T * p) template&lt;class T&gt; void operator()(T * p)

View File

@ -3154,7 +3154,6 @@ int main()
n_spt_intrusive::test(); n_spt_intrusive::test();
n_spt_another_sp::test(); n_spt_another_sp::test();
n_spt_shared_from_this::test(); n_spt_shared_from_this::test();
// n_spt_post_constructors::test();
n_spt_wrap::test(); n_spt_wrap::test();
return boost::report_errors(); return boost::report_errors();