diff --git a/shared_ptr.htm b/shared_ptr.htm index be047d7..6b5aa1c 100644 --- a/shared_ptr.htm +++ b/shared_ptr.htm @@ -8,11 +8,13 @@

c++boost.gif (8819 bytes)shared_ptr class template

Introduction
+ Best Practices
Synopsis
Members
Free Functions
Example
Handle/Body Idiom
+ Thread Safety
Frequently Asked Questions
Smart Pointer Timings

Introduction

@@ -44,6 +46,43 @@ to shared_ptr<T const>, to shared_ptr<U> where U is an accessible base of T, and to shared_ptr<void>.

+

Best Practices

+

A simple guideline that nearly eliminates the possibility of memory leaks + is: always use a named smart pointer variable to hold the result of new. + Every occurence of the new keyword in the code should have the + form:

+
shared_ptr<T> p(new Y);
+

It is, of course, acceptable to use another smart pointer in place of shared_ptr + above; having T and Y be the same type, or + passing arguments to Y's constructor is also OK.

+

If you observe this guideline, it naturally follows that you will have no + explicit deletes; try/catch constructs will + be rare.

+

Avoid using unnamed shared_ptr temporaries to save typing; to + see why this is dangerous, consider this example:

+
+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());
+}
+
+

The function ok follows the guideline to the letter, whereas + bad constructs the temporary shared_ptr in place, + admitting the possibility of a memory leak. Since function arguments are + evaluated in unspecified order, it is possible for new int(2) to + be evaluated first, g() second, and we may never get to the + shared_ptr constructor if g throws an exception. + See Herb Sutter's treatment of + the issue for more information.

Synopsis

namespace boost {
 
@@ -122,6 +161,22 @@
 			

Exception safety: If an exception is thrown, the constructor has no effect.

+

[The poscondition of use_count() == 1 is too strong. Having the nothrow + guarantee is important, since reset() 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 shared_ptr, including + all copies created from it, will probably be left unspecified.

+

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 shared_ptrs. 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.

+

A future release may enable shared_ptr construction from a + literal zero, for consistency with built-in pointers. It is not clear yet + whether this constructor should be left implicit, enabling 0 to + be used as a shorthand for shared_ptr<T>().]

template<typename Y> explicit shared_ptr(Y * p);

Requirements: p must be convertible to T *. Y @@ -138,6 +193,10 @@ use count is 1 holds even if p is 0; invoking delete on a pointer that has a value of 0 is harmless.

+

[This constructor has been changed to a template in order to remember the actual + pointer type passed. The destructor will call delete with the + same pointer, complete with its original type, even when T does + not have a virtual destructor, or is void.]

template<typename Y, typename D> shared_ptr(Y * p, D d);

Requirements: p must be convertible to T *. The copy @@ -152,6 +211,14 @@

Notes: When the the time comes to delete the object pointed to by p, d(p) is invoked.

+

[Custom deallocators allow a factory function returning a shared_ptr + 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 shared_ptr + to a statically allocated object.

+

The support for custom deallocators does not impose significant overhead. Other + shared_ptr features still require a deallocator to be kept.]

shared_ptr(shared_ptr const & r); // never throws
 template<typename Y> shared_ptr(shared_ptr<Y> const & r); // never throws
@@ -161,6 +228,8 @@ template<typename Y> shared_ptr(shared_ptr<Y> const & r); // nev increased by one.

Throws: nothing.

+

[The postcondition will be relaxed when a default-constructed shared_ptr + is being copied.]

explicit shared_ptr(weak_ptr const & r);

Effects: Constructs a shared_ptr, as if by storing a copy of the @@ -171,6 +240,17 @@ template<typename Y> shared_ptr(shared_ptr<Y> const & r); // nev

Exception safety: If an exception is thrown, the constructor has no effect.

+

[This constructor is an optional part of the specification; it depends on the + existence of weak_ptr. It is true that weak_ptr + support imposes overhead on every shared_ptr user, regardless + of whether weak pointers are used.

+

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 weak_ptr + interface is non-trivial.

+

My opinion is that the added functionality is worth the cost. weak_ptr + is provided in the reference implementation as a proof of concept.]

template<typename Y> shared_ptr(std::auto_ptr<Y> & r);

Effects: Constructs a shared_ptr, as if by storing a copy of r.release().

@@ -180,6 +260,9 @@ template<typename Y> shared_ptr(shared_ptr<Y> const & r); // nev

Exception safety: If an exception is thrown, the constructor has no effect.

+

[This constructor takes a the source auto_ptr by reference and + not by value, and cannot accept auto_ptr temporaries. This is + by design, as the constructor offers the strong guarantee.]

destructor

~shared_ptr(); // never throws
@@ -211,6 +294,8 @@ q = p;

Effects: Equivalent to shared_ptr().swap(*this).

+

[reset() will offer the nothrow guarantee in a future + implementation.]

template<typename Y> void reset(Y * p);

Effects: Equivalent to shared_ptr(p).swap(*this).

@@ -243,8 +328,12 @@ q = p;

Returns: use_count() == 1.

Throws: nothing.

-

Notes: unique() may be faster than use_count().

+

Notes: unique() may be faster than use_count(). + If you are using unique() to implement copy on write, do not rely + on a specific value when the stored pointer is zero.

+

[In a future release, unique() will return an unspecified value + for a default-constructed shared_ptr.]

use_count

long use_count() const; // never throws
@@ -255,16 +344,19 @@ q = p; for debugging and testing purposes, not for production code.

conversions

-
operator implementation-defined-type () const; // never throws
+
operator unspecified-bool-type () const; // never throws
-

Returns: an implementation defined value that, when used in boolean - contexts, is equivalent to get() != 0.

+

Returns: an unspecified value that, when used in boolean contexts, + is equivalent to get() != 0.

Throws: nothing.

Notes: This conversion operator allows shared_ptr objects to be used in boolean contexts, like if (p && p->valid()) {}. - 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.

+

[The conversion to bool is not merely syntactic sugar. It allows shared_ptrs + to be declared in conditions when using shared_dynamic_cast or + make_shared.]

swap

void swap(shared_ptr & b); // never throws
@@ -288,13 +380,19 @@ q = p;
template<typename T>
   bool operator<(shared_ptr<T> const & a, shared_ptr<T> const & b); // never throws
-

Returns: an implementation-defined value such that operator< is - a strict weak ordering as described in section 25.3 [lib.alg.sorting] +

Returns: an unspecified value such that operator< is a + strict weak ordering as described in section 25.3 [lib.alg.sorting] of the C++ standard.

Throws: nothing.

Notes: Allows shared_ptr objects to be used as keys in associative containers.

+

[Operator< has been preferred over a std::less + specialization for consistency and legality reasons, as std::less + is required to return the results of operator<, and many + standard algorithms use operator< instead of std::less + for comparisons when a predicate is not supplied.

+

The rest of the comparison operators are omitted by design.]

swap

template<typename T>
   void swap(shared_ptr<T> & a, shared_ptr<T> & b) // never throws
@@ -304,6 +402,9 @@ q = p;

Notes: Matches the interface of std::swap. Provided as an aid to generic programming.

+

[swap is defined in the same namespace as shared_ptr + as this is currently the only legal way to supply a swap function + that has a chance to be used by the standard library.]

shared_static_cast

template<typename T, typename U>
   shared_ptr<T> shared_static_cast(shared_ptr<U> const & r); // never throws
@@ -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 T be a complete type.

+

Thread Safety

+

shared_ptr objects offer the same level of thread safety as + built-in types. A shared_ptr instance can be "read" + (accessed using only const operations) simultaneously by multiple threads. + Different shared_ptr instances can be "written to" (accessed + using mutable operations such as operator= or reset) + simultaneosly by multiple threads (even when these instances are copies, and + share the same reference count underneath.)

+

Any other simultaneous accesses result in undefined behavior.

+

Examples:

+
+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
+
+

shared_ptr uses Boost.Config + to detect whether the implementation supports threads. If your program is + single-threaded, but your platform is autodetected by Boost.Config + as supporting multiple threads, #define BOOST_DISABLE_THREADS to + eliminate the thread safety overhead.

Frequently Asked Questions

Q. 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 Modern C++ Design by Andrei Alexandrescu.)

-

Q. I am not convinced. Default parameters can be used where appropriate to - hide the complexity. Again, why not policies?
+

Q. I am not convinced. Default parameters can be used where appropriate + to hide the complexity. Again, why not policies?
A. Template parameters affect the type. See the answer to the first question above.

Q. Why doesn't shared_ptr use a linked list implementation?
@@ -445,8 +605,7 @@ int * p = a.release(); implementation or a linked list implementation, or some other specific implementation. This is not the intent.


-

Revised  - 04 May 2002

+

Revised 23 July 2002

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