diff --git a/include/boost/shared_ptr.hpp b/include/boost/shared_ptr.hpp index c50f13c..3f988a7 100644 --- a/include/boost/shared_ptr.hpp +++ b/include/boost/shared_ptr.hpp @@ -106,7 +106,7 @@ public: typedef T * pointer; typedef typename detail::shared_ptr_traits::reference reference; - shared_ptr(): px(0), pn() + shared_ptr(): px(0), pn() // never throws in 1.30+ { } @@ -211,7 +211,7 @@ public: #endif - void reset() + void reset() // never throws in 1.30+ { this_type().swap(*this); } @@ -253,6 +253,8 @@ public: return px == 0? 0: &this_type::get; } + // operator! is redundant, but some compilers need it + bool operator! () const // never throws { return px == 0; diff --git a/shared_ptr.htm b/shared_ptr.htm index af055d3..6c7b012 100644 --- a/shared_ptr.htm +++ b/shared_ptr.htm @@ -8,13 +8,13 @@

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

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

Introduction

@@ -60,8 +60,7 @@ 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);
+		
void f(shared_ptr<int>, int);
 int g();
 
 void ok()
@@ -86,7 +85,7 @@ void bad()
 		

Synopsis

namespace boost {
 
-  class use_count_is_zero: public std::exception;
+  class bad_weak_ptr: public std::exception;
 
   template<class T> class weak_ptr;
 
@@ -96,21 +95,21 @@ void bad()
 
       typedef T element_type;
 
-      shared_ptr();
+      shared_ptr(); // never throws
       template<class Y> explicit shared_ptr(Y * p);
       template<class Y, class D> shared_ptr(Y * p, D d);
-      ~shared_ptr(); // never throws
+      ~shared_ptr(); // never throws
 
-      shared_ptr(shared_ptr const & r); // never throws
-      template<class Y> shared_ptr(shared_ptr<Y> const & r); // never throws
-      template<class Y> explicit shared_ptr(weak_ptr<Y> const & r);
+      shared_ptr(shared_ptr const & r); // never throws
+      template<class Y> shared_ptr(shared_ptr<Y> const & r); // never throws
+      template<class Y> explicit shared_ptr(weak_ptr<Y> const & r);
       template<class Y> explicit shared_ptr(std::auto_ptr<Y> & r);
 
       shared_ptr & operator=(shared_ptr const & r); // never throws  
       template<class Y> shared_ptr & operator=(shared_ptr<Y> const & r); // never throws
       template<class Y> shared_ptr & operator=(std::auto_ptr<Y> & r);
 
-      void reset();
+      void reset(); // never throws
       template<class Y> void reset(Y * p);
       template<class Y, class D> void reset(Y * p, D d);
 
@@ -121,37 +120,39 @@ void bad()
       bool unique() const; // never throws
       long use_count() const; // never throws
 
-      operator unspecified-bool-type() const; // never throws
+      operator unspecified-bool-type() const; // never throws
 
       void swap(shared_ptr & b); // never throws
   };
 
   template<class T, class U>
     bool operator==(shared_ptr<T> const & a, shared_ptr<U> const & b); // never throws
+
   template<class T, class U>
     bool operator!=(shared_ptr<T> const & a, shared_ptr<U> const & b); // never throws
-  template<class T>
-    bool operator<(shared_ptr<T> const & a, shared_ptr<T> const & b); // never throws
+
+  template<class T, class U>
+    bool operator<(shared_ptr<T> const & a, shared_ptr<U> const & b); // never throws
 
   template<class T> void swap(shared_ptr<T> & a, shared_ptr<T> & b); // never throws
 
   template<class T> T * get_pointer(shared_ptr<T> const & p); // never throws
 
   template<class T, class U>
-    shared_ptr<T> shared_static_cast(shared_ptr<U> const & r); // never throws
+    shared_ptr<T> static_pointer_cast(shared_ptr<U> const & r); // never throws
+
   template<class T, class U>
-    shared_ptr<T> shared_dynamic_cast(shared_ptr<U> const & r);
-  template<class T, class U>
-    shared_ptr<T> shared_polymorphic_cast(shared_ptr<U> const & r);
-  template<class T, class U>
-    shared_ptr<T> shared_polymorphic_downcast(shared_ptr<U> const & r); // never throws
+    shared_ptr<T> dynamic_pointer_cast(shared_ptr<U> const & r); // never throws
+
+  template<class E, class T, class Y>
+    std::basic_ostream<E, T> & operator<< (std::basic_ostream<E, T> & os, shared_ptr<Y> const & p);
 
 }

[It might be convenient to relax the requirements on shared_ptr's signature, allowing an additional, defaulted, template parameter; the parameter can encode the threading model, for example. This would help in detecting possible ODR violations.

-

On the other hand, using shared_ptr as an argument to a +

On the other hand, using shared_ptr as an argument to a template template parameter requires an exact signature match. Metaprogramming experts tend to deemphasize template template parameters as they are too inflexible, but the alternative is typically an std::allocator::rebind-type @@ -163,27 +164,22 @@ void bad()

Provides the type of the template parameter T.

constructors

-
shared_ptr();
+
shared_ptr(); // never throws
-

Effects: Constructs a shared_ptr.

-

Postconditions: use count is 1; the stored - pointer is 0.

-

Throws: std::bad_alloc.

-

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

+

Effects: Constructs an empty shared_ptr. Empty + shared_ptr objects have an unspecified use_count.

+

Postconditions: get() == 0.

+

Throws: nothing.

-

[The postcondition 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 +

[The nothrow guarantee is important, since reset() is specified + in terms of the default constructor; this implies that the constructor must not + allocate memory.

+

There are two possible 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.

+ by the specification. That's why the use_count() has been left unspecified.

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 @@ -195,8 +191,9 @@ void bad() well-formed, must not invoke undefined behavior, and must not throw exceptions.

Effects: Constructs a shared_ptr, storing a copy of p.

-

Postconditions: use count is 1.

-

Throws: std::bad_alloc.

+

Postconditions: use_count() == 1 && get() == p.

+

Throws: std::bad_alloc or an implementation-defined exception when + a resource other than memory could not be obtained.

Exception safety: If an exception is thrown, delete p is called.

Notes: p must be a pointer to an object that was @@ -208,28 +205,11 @@ void bad() 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.

-

In the current implementation, if p is convertible to counted_base - *, shared_ptr will use the embedded reference - count supplied by counted_base. This is an (experimental) - attempt to provide a way for shared_ptr to be constructed from - a raw pointer such as this. A free function shared_from_this(q) - performs the conversion when q is convertible to counted_base - const *.

-

The optional intrusive counting employed by the current implementation allows - shared_ptr to interoperate with intrusive_ptr, an - experimental generic intrusive-counted smart pointer.

-

Another possible implementation is to use a global pointer-to-count map instead - of intrusive counting. shared_from_this would no longer be - O(1), which is a concern for some users, although I do not expect any - performance problems, since the operation is rare. Maintaining a global map is - difficult; it needs to be initialized before any shared_ptr instances - are constructed, and the initialization needs to be thread safe. In addition, - under the Windows dynamic library model, it is possible for several maps to - exist.

-

It is not yet clear which implementation should be used, or whether the - specification should allow both; nevertheless, the ability to make a shared_ptr - from this is considered essential by experienced smart pointer - users.]

+

The optional intrusive counting support has been dropped as it exposes too much + implementation details and doesn't interact well with weak_ptr. + The current implementation uses a different mechanism, + enable_shared_from_this, to solve the "shared_ptr from + this" problem.]

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

Requirements: p must be convertible to T *. D @@ -238,8 +218,9 @@ void bad() well-formed, must not invoke undefined behavior, and must not throw exceptions.

Effects: Constructs a shared_ptr, storing a copy of p and d.

-

Postconditions: use count is 1.

-

Throws: std::bad_alloc.

+

Postconditions: use_count() == 1 && get() == p.

+

Throws: std::bad_alloc or an implementation-defined exception when + a resource other than memory could not be obtained.

Exception safety: If an exception is thrown, d(p) is called.

Notes: When the the time comes to delete the object pointed to by p, the stored copy of d is invoked with the stored copy of p @@ -250,38 +231,39 @@ void bad() 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.

+ to a statically allocated object, and other variations allow a shared_ptr + to be used as a wrapper for another smart pointer, easing interoperability.

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

The requirement that the copy constructor of D does not throw comes from the pass by value. If the copy constructor throws, the pointer is leaked. - Removing the requirement requires a pass by (const) reference. The problems are - that (1) pass by value conveniently changes functions (function references) to - function pointers (this has to be performed manually otherwise and some - compilers may not be able to do it) and (2) const references don't currently - (per the standard) bind to functions. This can be solved (I think) but it - requires an overload set that breaks on many compilers due to 14.5.5.2 problems - (and of course it will break on compilers that don't do partial ordering at - all.)

-

The requrement will be removed when the aforementioned issues are resolved.]

+ Removing the requirement requires a pass by (const) reference.

+

Pass by reference is problematic since (1) pass by value conveniently changes + functions (function references) to function pointers (this has to be performed + manually otherwise and some compilers may not be able to do it) and (2) const + references don't currently (per the standard) bind to functions. This can be + solved (I think) but it requires an overload set that breaks on many compilers + due to 14.5.5.2 problems (and of course it will break on compilers that don't + do partial ordering at all.)

+

The main problem with pass by reference, though, lies in its interaction with + rvalues. A const reference may still cause a copy, and will require a const + operator(). A non-const reference won't bind to an rvalue at all. A good + solution to this problem is the rvalue reference proposed in + N1377/N1385.]

shared_ptr(shared_ptr const & r); // never throws
 template<class Y> shared_ptr(shared_ptr<Y> const & r); // never throws
-

Effects: Constructs a shared_ptr, as if by storing a copy of the - pointer stored in r.

-

Postconditions: use count for all copies is - increased by one.

+

Effects: If r is empty, constructs an empty shared_ptr; + otherwise, constructs a shared_ptr that shares ownership with r.

+

Postconditions: get() == r.get().

Throws: nothing.

-

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

-
template<class Y> explicit shared_ptr(weak_ptr<Y> const & r);
+
template<class Y> explicit shared_ptr(weak_ptr<Y> const & r);
-

Effects: Constructs a shared_ptr, as if by storing a copy of the - pointer stored in r.

-

Postconditions: use count for all copies is - increased by one.

-

Throws: use_count_is_zero when r.use_count() == 0.

+

Effects: If r is empty, constructs an empty shared_ptr; + otherwise, constructs a shared_ptr that shares ownership with r + and stores a copy of the pointer stored in r.

+

Throws: bad_weak_ptr when r.use_count() == 0.

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

@@ -299,21 +281,22 @@ template<class Y> shared_ptr(shared_ptr<Y> const & r); // never
template<class Y> shared_ptr(std::auto_ptr<Y> & r);

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

-

Postconditions: use count is 1.

-

Throws: std::bad_alloc.

+

Postconditions: use_count() == 1.

+

Throws: std::bad_alloc or an implementation-defined exception when + a resource other than memory could not be obtained.

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

+ by design, as the constructor offers the strong guarantee; an rvalue reference + would solve this problem, too.]

destructor

~shared_ptr(); // never throws

Effects: If *this is the sole owner (use_count() == 1), - destroys the object pointed to by the stored pointer.

-

Postconditions: use count for all remaining - copies is decreased by one.

+ destroys the object pointed to by the stored pointer as specified at + construction time.

Throws: nothing.

assignment

@@ -327,8 +310,7 @@ template<class Y> shared_ptr & operator=(std::auto_ptr<Y> & and destruction are not considered observable side effects, and the implementation is free to meet the effects (and the implied guarantees) via different means, without creating a temporary. In particular, in the example:

-
-shared_ptr<int> p(new int);
+			
shared_ptr<int> p(new int);
 shared_ptr<void> q(p);
 p = p;
 q = p;
@@ -342,12 +324,10 @@ q = p;
 				actually applies here, so it's better to be explicit about the possible 
 				optimizations.]

reset

-
void reset();
+
void reset(); // never throws

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

-

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

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

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

@@ -384,13 +364,12 @@ q = p; 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
-

Returns: the number of shared_ptr objects sharing ownership of the - stored pointer.

+

Returns: the number of shared_ptr objects that share ownership + with *this, or an unspecified nonnegative value when *this + is empty.

Throws: nothing.

Notes: use_count() is not necessarily efficient. Use only for debugging and testing purposes, not for production code.

@@ -407,8 +386,8 @@ q = p; 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.]

+ to be declared in conditions when using dynamic_pointer_cast or + make_shared.]

swap

void swap(shared_ptr & b); // never throws
@@ -429,12 +408,18 @@ q = p;

Returns: a.get() != b.get().

Throws: nothing.

-
template<class T>
-  bool operator<(shared_ptr<T> const & a, shared_ptr<T> const & b); // never throws
+
template<class T, class U>
+  bool operator<(shared_ptr<T> const & a, shared_ptr<U> const & b); // never throws
-

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.

+

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; +
  • + under the equivalence relation defined by operator<, !(a + < b) && !(b < a), two shared_ptr instances + are equivalent if and only if they share ownership.

Throws: nothing.

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

@@ -468,23 +453,24 @@ q = p;

Notes: Provided as an aid to generic programming. Used by mem_fn.

-

shared_static_cast

+

static_pointer_cast

template<class T, class U>
-  shared_ptr<T> shared_static_cast(shared_ptr<U> const & r); // never throws
+ shared_ptr<T> static_pointer_cast(shared_ptr<U> const & r); // never throws

Requires: The expression static_cast<T*>(r.get()) must be well-formed.

-

Returns: A shared_ptr<T> object that stores a copy - of static_cast<T*>(r.get()) and shares ownership with r.

+

Returns: If r is empty, an empty shared_ptr<T>; + otherwise, a shared_ptr<T> object that stores a copy of + static_cast<T*>(r.get()) and shares ownership with r.

Throws: nothing.

Notes: the seemingly equivalent expression

shared_ptr<T>(static_cast<T*>(r.get()))

will eventually result in undefined behavior, attempting to delete the same object twice.

-

shared_dynamic_cast

+

dynamic_pointer_cast

template<class T, class U>
-  shared_ptr<T> shared_dynamic_cast(shared_ptr<U> const & r);
+ shared_ptr<T> dynamic_pointer_cast(shared_ptr<U> const & r);

Requires: The expression dynamic_cast<T*>(r.get()) must be well-formed and its behavior defined.

@@ -492,42 +478,22 @@ q = p;
  • When dynamic_cast<T*>(r.get()) returns a nonzero value, a - shared_ptr<T> object that stores a copy of it and shares - ownership with r; + shared_ptr<T> object that stores a copy of it and shares + ownership with r;
  • - Otherwise, a default-constructed shared_ptr<T> object.
-

Throws: std::bad_alloc.

-

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

+ Otherwise, an empty shared_ptr<T> object. +

Throws: nothing.

Notes: the seemingly equivalent expression

shared_ptr<T>(dynamic_cast<T*>(r.get()))

will eventually result in undefined behavior, attempting to delete the same object twice.

-

shared_polymorphic_cast

-
template<class T, class U>
-  shared_ptr<T> shared_polymorphic_cast(shared_ptr<U> const & r);
+

operator<<

+
template<class E, class T, class Y>
+    std::basic_ostream<E, T> & operator<< (std::basic_ostream<E, T> & os, shared_ptr<Y> const & p);
-

Requires: The expression - polymorphic_cast<T*>(r.get()) must be well-formed and - its behavior defined.

-

Returns: A shared_ptr<T> object that stores a copy - of polymorphic_cast<T*>(r.get()) - and shares ownership with r.

-

Throws: std::bad_cast when the pointer cannot be - converted.

-

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

-
-

shared_polymorphic_downcast

-
template<class T, class U>
-  shared_ptr<T> shared_polymorphic_downcast(shared_ptr<U> const & r); // never throws
-
-

Requires: The expression - polymorphic_downcast<T*>(r.get()) must be well-formed - and its behavior defined.

-

Returns: A shared_ptr<T> object that stores a copy - of polymorphic_downcast<T*>(r.get()) - and shares ownership with r.

-

Throws: nothing.

+

Effects: os << p.get();.

+

Returns: os.

Example

See shared_ptr_example.cpp for a complete @@ -563,8 +529,7 @@ q = p; underneath.)

Any other simultaneous accesses result in undefined behavior.

Examples:

-
-shared_ptr<int> p(new int(42));
+		
shared_ptr<int> p(new int(42));
 
 //--- Example 1 ---
 
@@ -577,7 +542,6 @@ shared_ptr<int> p3(p); // OK, multiple reads are safe
 //--- Example 2 ---
 
 // thread A
-
 p.reset(new int(1912)); // writes p
 
 // thread B
@@ -668,11 +632,12 @@ int * p = a.release();
 			implementation or a linked list implementation, or some other specific 
 			implementation. This is not the intent.


-

Revised $Date$

+

+ $Date$

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 - all copies. This document is provided "as is" without express or implied + Copyright 2002, 2003 Peter Dimov. Permission to copy, use, modify, sell + and distribute this document is granted provided this copyright notice appears + in all copies. This document is provided "as is" without express or implied warranty, and with no claim as to its suitability for any purpose.