forked from boostorg/smart_ptr
Best practices section, thread safety section, design notes added.
[SVN r14572]
This commit is contained in:
181
shared_ptr.htm
181
shared_ptr.htm
@ -8,11 +8,13 @@
|
||||
<h1><IMG height="86" alt="c++boost.gif (8819 bytes)" src="../../c++boost.gif" width="277" align="middle">shared_ptr
|
||||
class template</h1>
|
||||
<p><A href="#Introduction">Introduction</A><br>
|
||||
<a href="#BestPractices">Best Practices</a><br>
|
||||
<A href="#Synopsis">Synopsis</A><br>
|
||||
<A href="#Members">Members</A><br>
|
||||
<A href="#functions">Free Functions</A><br>
|
||||
<A href="#example">Example</A><br>
|
||||
<A href="#Handle/Body">Handle/Body Idiom</A><br>
|
||||
<a href="#ThreadSafety">Thread Safety</a><br>
|
||||
<A href="#FAQ">Frequently Asked Questions</A><br>
|
||||
<A href="smarttests.htm">Smart Pointer Timings</A></p>
|
||||
<h2><a name="Introduction">Introduction</a></h2>
|
||||
@ -44,6 +46,43 @@
|
||||
to <STRONG>shared_ptr<T const></STRONG>, to <STRONG>shared_ptr<U></STRONG>
|
||||
where <STRONG>U</STRONG> is an accessible base of <STRONG>T</STRONG>, and to <STRONG>
|
||||
shared_ptr<void></STRONG>.</P>
|
||||
<h2><a name="BestPractices">Best Practices</a></h2>
|
||||
<P>A simple guideline that nearly eliminates the possibility of memory leaks
|
||||
is: always use a named smart pointer variable to hold the result of <STRONG>new. </STRONG>
|
||||
Every occurence of the <STRONG>new</STRONG> keyword in the code should have the
|
||||
form:</P>
|
||||
<PRE>shared_ptr<T> p(new Y);</PRE>
|
||||
<P>It is, of course, acceptable to use another smart pointer in place of <STRONG>shared_ptr</STRONG>
|
||||
above; having <STRONG>T</STRONG> and <STRONG>Y</STRONG> be the same type, or
|
||||
passing arguments to <STRONG>Y</STRONG>'s constructor is also OK.</P>
|
||||
<P>If you observe this guideline, it naturally follows that you will have no
|
||||
explicit <STRONG>delete</STRONG>s; <STRONG>try/catch</STRONG> constructs will
|
||||
be rare.</P>
|
||||
<P>Avoid using unnamed <STRONG>shared_ptr</STRONG> temporaries to save typing; to
|
||||
see why this is dangerous, consider this example:</P>
|
||||
<PRE>
|
||||
void f(shared_ptr<int>, int);
|
||||
int g();
|
||||
|
||||
void ok()
|
||||
{
|
||||
shared_ptr<int> p(new int(2));
|
||||
f(p, g());
|
||||
}
|
||||
|
||||
void bad()
|
||||
{
|
||||
f(shared_ptr<int>(new int(2)), g());
|
||||
}
|
||||
</PRE>
|
||||
<P>The function <STRONG>ok</STRONG> follows the guideline to the letter, whereas <STRONG>
|
||||
bad</STRONG> constructs the temporary <STRONG>shared_ptr</STRONG> in place,
|
||||
admitting the possibility of a memory leak. Since function arguments are
|
||||
evaluated in unspecified order, it is possible for <STRONG>new int(2)</STRONG> to
|
||||
be evaluated first, <STRONG>g()</STRONG> second, and we may never get to the <STRONG>
|
||||
shared_ptr </STRONG>constructor if <STRONG>g</STRONG> throws an exception.
|
||||
See <A href="http://www.gotw.ca/gotw/056.htm">Herb Sutter's treatment</A> of
|
||||
the issue for more information.</P>
|
||||
<h2><a name="Synopsis">Synopsis</a></h2>
|
||||
<pre>namespace boost {
|
||||
|
||||
@ -122,6 +161,22 @@
|
||||
<p><b>Exception safety:</b> If an exception is thrown, the constructor has no
|
||||
effect.</p>
|
||||
</blockquote>
|
||||
<P><EM>[The poscondition of use_count() == 1 is too strong. Having the nothrow
|
||||
guarantee is important, since <STRONG>reset()</STRONG> is specified in terms of
|
||||
the default constructor, but the current specification requires that a count be
|
||||
allocated. Therefore, this postcondition will be dropped in a future release.
|
||||
The use count of a default-constructed <STRONG>shared_ptr</STRONG>, including
|
||||
all copies created from it, will probably be left unspecified.</EM></P>
|
||||
<P><EM>There are two possible nothrow implementations, one stores 0 as a pointer to the
|
||||
reference count, the other uses a single statically allocated count for all
|
||||
default-constructed <STRONG>shared_ptr</STRONG>s. The second option is
|
||||
difficult to achieve in the current header-only reference implementation due to
|
||||
thread safety issues and initialization order, but it should not be precluded
|
||||
by the specification.</EM></P>
|
||||
<P><EM>A future release may enable <STRONG>shared_ptr</STRONG> construction from a
|
||||
literal zero, for consistency with built-in pointers. It is not clear yet
|
||||
whether this constructor should be left implicit, enabling <STRONG>0</STRONG> to
|
||||
be used as a shorthand for <STRONG>shared_ptr<T>().</STRONG>]</EM></P>
|
||||
<pre>template<typename Y> explicit shared_ptr(Y * p);</pre>
|
||||
<blockquote>
|
||||
<p><b>Requirements:</b> <b>p</b> must be convertible to <b>T *</b>. <STRONG>Y</STRONG>
|
||||
@ -138,6 +193,10 @@
|
||||
use count</A> is 1 holds even if <b>p</b> is 0; invoking <STRONG>delete</STRONG>
|
||||
on a pointer that has a value of 0 is harmless.</P>
|
||||
</blockquote>
|
||||
<P><EM>[This constructor has been changed to a template in order to remember the actual
|
||||
pointer type passed. The destructor will call <STRONG>delete</STRONG> with the
|
||||
same pointer, complete with its original type, even when <STRONG>T</STRONG> does
|
||||
not have a virtual destructor, or is <STRONG>void</STRONG>.]</EM></P>
|
||||
<pre>template<typename Y, typename D> shared_ptr(Y * p, D d);</pre>
|
||||
<blockquote>
|
||||
<p><b>Requirements:</b> <B>p</B> must be convertible to <B>T *</B>. The copy
|
||||
@ -152,6 +211,14 @@
|
||||
<p><b>Notes:</b> When the the time comes to delete the object pointed to by <b>p</b>,
|
||||
<code>d(p)</code> is invoked.</p>
|
||||
</blockquote>
|
||||
<P><EM>[Custom deallocators allow a factory function returning a <STRONG>shared_ptr</STRONG>
|
||||
to insulate the user from its memory allocation strategy. Since the deallocator
|
||||
is not part of the type, changing the allocation strategy does not break source
|
||||
or binary compatibility, and does not require a client recompilation. For
|
||||
example, a "no-op" deallocator is useful when returning a <STRONG>shared_ptr</STRONG>
|
||||
to a statically allocated object.</EM></P>
|
||||
<P><EM>The support for custom deallocators does not impose significant overhead. Other <STRONG>
|
||||
shared_ptr</STRONG> features still require a deallocator to be kept.]</EM></P>
|
||||
<pre>shared_ptr(shared_ptr const & r); // never throws
|
||||
template<typename Y> shared_ptr(shared_ptr<Y> const & r); // never throws</pre>
|
||||
<blockquote>
|
||||
@ -161,6 +228,8 @@ template<typename Y> shared_ptr(shared_ptr<Y> const & r); // nev
|
||||
increased by one.</p>
|
||||
<p><b>Throws:</b> nothing.</p>
|
||||
</blockquote>
|
||||
<P><EM>[The postcondition will be relaxed when a default-constructed <STRONG>shared_ptr</STRONG>
|
||||
is being copied.]</EM></P>
|
||||
<pre>explicit shared_ptr(<A href="weak_ptr.htm" >weak_ptr</A> const & r);</pre>
|
||||
<blockquote>
|
||||
<p><b>Effects:</b> Constructs a <b>shared_ptr</b>, as if by storing a copy of the
|
||||
@ -171,6 +240,17 @@ template<typename Y> shared_ptr(shared_ptr<Y> const & r); // nev
|
||||
<p><b>Exception safety:</b> If an exception is thrown, the constructor has no
|
||||
effect.</p>
|
||||
</blockquote>
|
||||
<P><EM>[This constructor is an optional part of the specification; it depends on the
|
||||
existence of <STRONG>weak_ptr</STRONG>. It is true that <STRONG>weak_ptr</STRONG>
|
||||
support imposes overhead on every <STRONG>shared_ptr</STRONG> user, regardless
|
||||
of whether weak pointers are used.</EM></P>
|
||||
<P><EM>On the other hand, cyclic references are a serious problem with all reference
|
||||
counted designs. Not providing a solution within the library is unacceptable;
|
||||
if users are forced to reinvent the weak pointer wheel, there is substantial
|
||||
probability that they will get it wrong, as designing a safe <STRONG>weak_ptr</STRONG>
|
||||
interface is non-trivial.</EM></P>
|
||||
<P><EM>My opinion is that the added functionality is worth the cost. <STRONG>weak_ptr</STRONG>
|
||||
is provided in the reference implementation as a proof of concept.]</EM></P>
|
||||
<pre>template<typename Y> shared_ptr(std::auto_ptr<Y> & r);</pre>
|
||||
<BLOCKQUOTE>
|
||||
<P><B>Effects:</B> Constructs a <B>shared_ptr</B>, as if by storing a copy of <STRONG>r.release()</STRONG>.</P>
|
||||
@ -180,6 +260,9 @@ template<typename Y> shared_ptr(shared_ptr<Y> const & r); // nev
|
||||
<P><B>Exception safety:</B> If an exception is thrown, the constructor has no
|
||||
effect.</P>
|
||||
</BLOCKQUOTE>
|
||||
<P><EM>[This constructor takes a the source <STRONG>auto_ptr</STRONG> by reference and
|
||||
not by value, and cannot accept <STRONG>auto_ptr</STRONG> temporaries. This is
|
||||
by design, as the constructor offers the strong guarantee.]</EM></P>
|
||||
<h3><a name="destructor">destructor</a></h3>
|
||||
<pre>~shared_ptr(); // never throws</pre>
|
||||
<BLOCKQUOTE>
|
||||
@ -211,6 +294,8 @@ q = p;
|
||||
<BLOCKQUOTE>
|
||||
<P><B>Effects:</B> Equivalent to <code>shared_ptr().swap(*this)</code>.</P>
|
||||
</BLOCKQUOTE>
|
||||
<P><EM>[<STRONG>reset()</STRONG> will offer the nothrow guarantee in a future
|
||||
implementation.]</EM></P>
|
||||
<pre>template<typename Y> void reset(Y * p);</pre>
|
||||
<BLOCKQUOTE>
|
||||
<P><B>Effects:</B> Equivalent to <code>shared_ptr(p).swap(*this)</code>.</P>
|
||||
@ -243,8 +328,12 @@ q = p;
|
||||
<blockquote>
|
||||
<p><b>Returns:</b> <code>use_count() == 1</code>.</p>
|
||||
<p><b>Throws:</b> nothing.</p>
|
||||
<P><B>Notes:</B> <code>unique()</code> may be faster than <code>use_count()</code>.</P>
|
||||
<P><B>Notes:</B> <code>unique()</code> may be faster than <code>use_count()</code>.
|
||||
If you are using <code>unique()</code> to implement copy on write, do not rely
|
||||
on a specific value when the stored pointer is zero.</P>
|
||||
</blockquote>
|
||||
<P><EM>[In a future release, <STRONG>unique()</STRONG> will return an unspecified value
|
||||
for a default-constructed <STRONG>shared_ptr.</STRONG>]</EM></P>
|
||||
<h3><a name="use_count">use_count</a></h3>
|
||||
<pre>long use_count() const; // never throws</pre>
|
||||
<blockquote>
|
||||
@ -255,16 +344,19 @@ q = p;
|
||||
for debugging and testing purposes, not for production code.</P>
|
||||
</blockquote>
|
||||
<h3><a name="conversions">conversions</a></h3>
|
||||
<pre>operator <i>implementation-defined-type</i> () const; // never throws</pre>
|
||||
<pre>operator <i>unspecified-bool-type</i> () const; // never throws</pre>
|
||||
<blockquote>
|
||||
<p><b>Returns:</b> an implementation defined value that, when used in boolean
|
||||
contexts, is equivalent to <code>get() != 0</code>.</p>
|
||||
<p><b>Returns:</b> an unspecified value that, when used in boolean contexts,
|
||||
is equivalent to <code>get() != 0</code>.</p>
|
||||
<p><b>Throws:</b> nothing.</p>
|
||||
<P><B>Notes:</B> This conversion operator allows <b>shared_ptr</b> objects to be
|
||||
used in boolean contexts, like <code>if (p && p->valid()) {}</code>.
|
||||
The actual target type is typically a pointer to a member function, avloiding
|
||||
The actual target type is typically a pointer to a member function, avoiding
|
||||
many of the implicit conversion pitfalls.</P>
|
||||
</blockquote>
|
||||
<P><EM>[The conversion to bool is not merely syntactic sugar. It allows <STRONG>shared_ptr</STRONG>s
|
||||
to be declared in conditions when using <STRONG>shared_dynamic_cast </STRONG>or <STRONG>
|
||||
make_shared</STRONG>.]</EM></P>
|
||||
<h3><a name="swap">swap</a></h3>
|
||||
<pre>void swap(shared_ptr & b); // never throws</pre>
|
||||
<blockquote>
|
||||
@ -288,13 +380,19 @@ q = p;
|
||||
<pre>template<typename T>
|
||||
bool operator<(shared_ptr<T> const & a, shared_ptr<T> const & b); // never throws</pre>
|
||||
<blockquote>
|
||||
<p><b>Returns:</b> an implementation-defined value such that <b>operator<</b> is
|
||||
a strict weak ordering as described in section 25.3 <code>[lib.alg.sorting]</code>
|
||||
<p><b>Returns:</b> an unspecified value such that <b>operator<</b> is a
|
||||
strict weak ordering as described in section 25.3 <code>[lib.alg.sorting]</code>
|
||||
of the C++ standard.</p>
|
||||
<p><b>Throws:</b> nothing.</p>
|
||||
<P><B>Notes:</B> Allows <STRONG>shared_ptr</STRONG> objects to be used as keys in
|
||||
associative containers.</P>
|
||||
</blockquote>
|
||||
<P><EM>[<STRONG>Operator<</STRONG> has been preferred over a <STRONG>std::less </STRONG>
|
||||
specialization for consistency and legality reasons, as <STRONG>std::less</STRONG>
|
||||
is required to return the results of <STRONG>operator<</STRONG>, and many
|
||||
standard algorithms use <STRONG>operator<</STRONG> instead of <STRONG>std::less</STRONG>
|
||||
for comparisons when a predicate is not supplied.</EM></P>
|
||||
<P><EM>The rest of the comparison operators are omitted by design.]</EM></P>
|
||||
<h3><a name="free-swap">swap</a></h3>
|
||||
<pre>template<typename T>
|
||||
void swap(shared_ptr<T> & a, shared_ptr<T> & b) // never throws</pre>
|
||||
@ -304,6 +402,9 @@ q = p;
|
||||
<P><B>Notes:</B> Matches the interface of <B>std::swap</B>. Provided as an aid to
|
||||
generic programming.</P>
|
||||
</BLOCKQUOTE>
|
||||
<P><EM>[<STRONG>swap</STRONG> is defined in the same namespace as <STRONG>shared_ptr</STRONG>
|
||||
as this is currently the only legal way to supply a <STRONG>swap</STRONG> function
|
||||
that has a chance to be used by the standard library.]</EM></P>
|
||||
<h3><a name="shared_static_cast">shared_static_cast</a></h3>
|
||||
<pre>template<typename T, typename U>
|
||||
shared_ptr<T> shared_static_cast(shared_ptr<U> const & r); // never throws</pre>
|
||||
@ -389,6 +490,65 @@ q = p;
|
||||
implementation file. Note that there is no need for an explicit destructor.
|
||||
Unlike ~scoped_ptr, ~shared_ptr does not require that <b>T</b> be a complete
|
||||
type.</p>
|
||||
<h2><a name="ThreadSafety">Thread Safety</a></h2>
|
||||
<p><STRONG>shared_ptr</STRONG> objects offer the same level of thread safety as
|
||||
built-in types. A <STRONG>shared_ptr</STRONG> instance can be "read"
|
||||
(accessed using only const operations) simultaneously by multiple threads.
|
||||
Different <STRONG>shared_ptr</STRONG> instances can be "written to" (accessed
|
||||
using mutable operations such as <STRONG>operator= </STRONG>or <STRONG>reset</STRONG>)
|
||||
simultaneosly by multiple threads (even when these instances are copies, and
|
||||
share the same reference count underneath.)</p>
|
||||
<P>Any other simultaneous accesses result in undefined behavior.</P>
|
||||
<P>Examples:</P>
|
||||
<pre>
|
||||
shared_ptr<int> p(new int(42));
|
||||
|
||||
//--- Example 1 ---
|
||||
|
||||
// thread A
|
||||
shared_ptr<int> p2(p); // reads p
|
||||
|
||||
// thread B
|
||||
shared_ptr<int> p3(p); // OK, multiple reads are safe
|
||||
|
||||
//--- Example 2 ---
|
||||
|
||||
// thread A
|
||||
|
||||
p.reset(new int(1912)); // writes p
|
||||
|
||||
// thread B
|
||||
p2.reset(); // OK, writes p2
|
||||
|
||||
//--- Example 3 ---
|
||||
|
||||
// thread A
|
||||
p = p3; // reads p3, writes p
|
||||
|
||||
// thread B
|
||||
p3.reset(); // writes p3; undefined, simultaneous read/write
|
||||
|
||||
//--- Example 4 ---
|
||||
|
||||
// thread A
|
||||
p3 = p2; // reads p2, writes p3
|
||||
|
||||
// thread B
|
||||
// p2 goes out of scope: undefined, the destructor is considered a "write access"
|
||||
|
||||
//--- Example 5 ---
|
||||
|
||||
// thread A
|
||||
p3.reset(new int(1));
|
||||
|
||||
// thread B
|
||||
p3.reset(new int(2)); // undefined, multiple writes
|
||||
</pre>
|
||||
<p><STRONG>shared_ptr</STRONG> uses <A href="../config/config.htm">Boost.Config</A>
|
||||
to detect whether the implementation supports threads. If your program is
|
||||
single-threaded, but your platform is autodetected by <STRONG>Boost.Config</STRONG>
|
||||
as supporting multiple threads, <STRONG>#define BOOST_DISABLE_THREADS</STRONG> to
|
||||
eliminate the thread safety overhead.</p>
|
||||
<h2><a name="FAQ">Frequently Asked Questions</a></h2>
|
||||
<P><B>Q.</B> There are several variations of shared pointers, with different
|
||||
tradeoffs; why does the smart pointer library supply only a single
|
||||
@ -408,8 +568,8 @@ q = p;
|
||||
pointer of choice for a wide range of applications. (Those interested in policy
|
||||
based smart pointers should read <A href="http://cseng.aw.com/book/0,,0201704315,00.html">
|
||||
Modern C++ Design</A> by Andrei Alexandrescu.)</P>
|
||||
<P><B>Q.</B> I am not convinced. Default parameters can be used where appropriate to
|
||||
hide the complexity. Again, why not policies?<BR>
|
||||
<P><B>Q.</B> I am not convinced. Default parameters can be used where appropriate
|
||||
to hide the complexity. Again, why not policies?<BR>
|
||||
<B>A.</B> Template parameters affect the type. See the answer to the first
|
||||
question above.</P>
|
||||
<p><b>Q.</b> Why doesn't <b>shared_ptr</b> use a linked list implementation?<br>
|
||||
@ -445,8 +605,7 @@ int * p = a.release();
|
||||
implementation or a linked list implementation, or some other specific
|
||||
implementation. This is not the intent.</p>
|
||||
<hr>
|
||||
<p>Revised <!--webbot bot="Timestamp" S-Type="EDITED" S-Format="%d %B %Y" startspan -->
|
||||
04 May 2002<!--webbot bot="Timestamp" i-checksum="38439" endspan --></p>
|
||||
<p>Revised <!--webbot bot="Timestamp" S-Type="EDITED" S-Format="%d %B %Y" startspan -->23 July 2002<!--webbot bot="Timestamp" i-checksum="38439" endspan --></p>
|
||||
<p>Copyright 1999 Greg Colvin and Beman Dawes. Copyright 2002 Darin Adler.
|
||||
Copyright 2002 Peter Dimov. Permission to copy, use, modify, sell and
|
||||
distribute this document is granted provided this copyright notice appears in
|
||||
|
Reference in New Issue
Block a user