From 3e616752c9900b94026bf119699bce731401a45c Mon Sep 17 00:00:00 2001 From: Peter Dimov Date: Sat, 4 Jan 2003 14:24:14 +0000 Subject: [PATCH] weak_ptr documentation updated; still a work in progress. [SVN r16748] --- include/boost/shared_ptr.hpp | 2 +- include/boost/weak_ptr.hpp | 6 +- shared_ptr.htm | 92 ++++++++----- weak_ptr.htm | 251 +++++++++++++++++------------------ 4 files changed, 186 insertions(+), 165 deletions(-) diff --git a/include/boost/shared_ptr.hpp b/include/boost/shared_ptr.hpp index 3f988a7..debb9a7 100644 --- a/include/boost/shared_ptr.hpp +++ b/include/boost/shared_ptr.hpp @@ -5,7 +5,7 @@ // shared_ptr.hpp // // (C) Copyright Greg Colvin and Beman Dawes 1998, 1999. -// Copyright (c) 2001, 2002 Peter Dimov +// Copyright (c) 2001, 2002, 2003 Peter Dimov // // Permission to copy, use, modify, sell and distribute this software // is granted provided this copyright notice appears in all copies. diff --git a/include/boost/weak_ptr.hpp b/include/boost/weak_ptr.hpp index 8c2a977..9d56858 100644 --- a/include/boost/weak_ptr.hpp +++ b/include/boost/weak_ptr.hpp @@ -4,7 +4,7 @@ // // weak_ptr.hpp // -// Copyright (c) 2001, 2002 Peter Dimov +// Copyright (c) 2001, 2002, 2003 Peter Dimov // // Permission to copy, use, modify, sell and distribute this software // is granted provided this copyright notice appears in all copies. @@ -37,7 +37,7 @@ public: typedef T element_type; - weak_ptr(): px(0), pn() + weak_ptr(): px(0), pn() // never throws in 1.30+ { } @@ -92,7 +92,7 @@ public: #endif - void reset() + void reset() // never throws in 1.30+ { this_type().swap(*this); } diff --git a/shared_ptr.htm b/shared_ptr.htm index 6c7b012..b2f9dcf 100644 --- a/shared_ptr.htm +++ b/shared_ptr.htm @@ -8,6 +8,7 @@

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

Introduction
+ Motivation
Best Practices
Synopsis
Members
@@ -46,6 +47,8 @@ to shared_ptr<T const>, to shared_ptr<U> where U is an accessible base of T, and to shared_ptr<void>.

+

Motivation

+

[...]

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. @@ -262,22 +265,11 @@ template<class Y> shared_ptr(shared_ptr<Y> const & r); // never

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.

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

-

[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<class Y> shared_ptr(std::auto_ptr<Y> & r);

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

@@ -580,45 +572,60 @@ p3.reset(new int(2)); // undefined, multiple writes

Q. There are several variations of shared pointers, with different tradeoffs; why does the smart pointer library supply only a single implementation? It would be useful to be able to experiment with each type so - as to find the most suitable for the job at hand?
+ as to find the most suitable for the job at hand?

+

A. An important goal of shared_ptr is to provide a standard shared-ownership pointer. Having a single pointer type is important for stable library interfaces, since different shared pointers typically cannot interoperate, i.e. a reference counted pointer (used by library A) cannot share - ownership with a linked pointer (used by library B.)

+ ownership with a linked pointer (used by library B.)
+

Q. Why doesn't shared_ptr have template parameters supplying - traits or policies to allow extensive user customization?
+ traits or policies to allow extensive user customization?

+

A. Parameterization discourages users. The shared_ptr template is carefully crafted to meet common needs without extensive parameterization. Some day a highly configurable smart pointer may be invented that is also very easy to use and very hard to misuse. Until then, shared_ptr is the smart pointer of choice for a wide range of applications. (Those interested in policy based smart pointers should read - Modern C++ Design by Andrei Alexandrescu.)

+ 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?
+ 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?
+ question above.
+

+

Q. Why doesn't shared_ptr use a linked list implementation?

+

A. A linked list implementation does not offer enough advantages to offset the added cost of an extra pointer. See timings page. In addition, it is expensive to make a linked list implementation thread - safe.

-

Q. Why doesn't shared_ptr (or any of the other Boost smart - pointers) supply an automatic conversion to T*?
- A. Automatic conversion is believed to be too error prone.

-

Q. Why does shared_ptr supply use_count()?
+ safe.
+

+

Q. Why doesn't shared_ptr (or any of the other Boost smart + pointers) supply an automatic conversion to T*?

+

+ A. Automatic conversion is believed to be too error prone.
+

+

Q. Why does shared_ptr supply use_count()?

+

A. As an aid to writing test cases and debugging displays. One of the progenitors had use_count(), and it was useful in tracking down bugs in a - complex project that turned out to have cyclic-dependencies.

-

Q. Why doesn't shared_ptr specify complexity requirements?
+ complex project that turned out to have cyclic-dependencies.
+

+

Q. Why doesn't shared_ptr specify complexity requirements?

+

A. Because complexity requirements limit implementors and complicate the specification without apparent benefit to shared_ptr users. For example, error-checking implementations might become non-conforming if they had to meet - stringent complexity requirements.

-

Q. Why doesn't shared_ptr provide a release() function?
+ stringent complexity requirements.
+

+

Q. Why doesn't shared_ptr provide a release() function?

+

A. shared_ptr cannot give away ownership unless it's unique() - because the other copy will still destroy the object.

+ because the other copy will still destroy the object.

Consider:

shared_ptr<int> a(new int);
 shared_ptr<int> b(a); // a.use_count() == b.use_count() == 2
@@ -627,17 +634,32 @@ int * p = a.release();
 
 // Who owns p now? b will still call delete on it in its destructor.
-

Q. Why doesn't shared_ptr provide (your pet feature here)?
+

Furthermore, the pointer returned by release() would be difficult + to deallocate reliably, as the source shared_ptr could have been created + with a custom deleter.
+

+

Q. Why is operator->() const, but its return value is a + non-const pointer to the element type?

+

+ A. Shallow copy pointers, including raw pointers, typically don't + propagate constness. It makes little sense for them to do so, as you can always + obtain a non-const pointer from a const one and then proceed to modify the + object through it.shared_ptr is "as close to raw pointers as possible + but no closer".
+

+

Q. Why doesn't shared_ptr provide (your pet feature here)?

+

A. Because (your pet feature here) would mandate a reference counted implementation or a linked list implementation, or some other specific - implementation. This is not the intent.

+ implementation. This is not the intent.
+


$Date$

Copyright 1999 Greg Colvin and Beman Dawes. Copyright 2002 Darin Adler. - 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 + 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.

diff --git a/weak_ptr.htm b/weak_ptr.htm index d0273b4..1bafb3e 100644 --- a/weak_ptr.htm +++ b/weak_ptr.htm @@ -4,32 +4,39 @@ weak_ptr - -

c++boost.gif (8819 bytes)weak_ptr + +

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

-

The weak_ptr class template stores a "weak reference" to an - object that's already managed by a shared_ptr. To access the object, a - weak_ptr can be converted to a shared_ptr using +

Introduction
+ Motivation
+ Synopsis
+ Members
+ Free Functions
+ Frequently Asked Questions +

+

Introduction

+

The weak_ptr class template stores a "weak reference" to an object that's + already managed by a shared_ptr. To access the object, a weak_ptr + can be converted to a shared_ptr using the shared_ptr constructor or the function make_shared. When the last shared_ptr to the object - goes away and the object is deleted, the attempt to obtain a shared_ptr -  from the weak_ptr instances that refer to the deleted object will - fail: the constructor will throw an exception of type boost::use_count_is_zero, - and make_shared will return a default constructed (null) shared_ptr.

+ goes away and the object is deleted, the attempt to obtain a shared_ptr + from the weak_ptr instances that refer to the deleted object will fail: + the constructor will throw an exception of type boost::bad_weak_ptr, + and make_shared will return an empty shared_ptr.

Every weak_ptr meets the CopyConstructible and Assignable requirements of the C++ Standard Library, and so can be used in standard library containers. Comparison operators are supplied so that weak_ptr works with the standard library's associative containers.

+

weak_ptr operations never throw exceptions.

The class template is parameterized on T, the type of the object pointed - to. T must meet the smart pointer - common requirements.

-

Compared to shared_ptr, weak_ptr provides - a very limited subset of operations since accessing its stored pointer is - often dangerous in multithreaded programs, and sometimes unsafe even - within a single thread (that is, it may invoke undefined behavior.) - Consider, for example, this innocent piece of code:

-
-shared_ptr<int> p(new int(5));
+			to.

+

Compared to shared_ptr, weak_ptr provides a + very limited subset of operations since accessing its stored pointer is often + dangerous in multithreaded programs, and sometimes unsafe even within a single + thread (that is, it may invoke undefined behavior.) Consider, for example, this + innocent piece of code:

+
shared_ptr<int> p(new int(5));
 weak_ptr<int> q(p);
 
 // some time later
@@ -44,13 +51,12 @@ if(int * r = q.get())
 			is a dangling pointer.

The solution to this problem is to create a temporary shared_ptr from q:

-
-shared_ptr<int> p(new int(5));
+		
shared_ptr<int> p(new int(5));
 weak_ptr<int> q(p);
 
 // some time later
 
-if(shared_ptr<int> r = make_shared(q))
+if(shared_ptr<int> r = make_shared(q))
 {
     // use *r
 }
@@ -58,45 +64,53 @@ if(shared_ptr<int> r = make_shared(q))
 		

Now r holds a reference to the object that was pointed by q. Even if p.reset() is executed in another thread, the object will stay alive until r goes out of scope (or is reset.)

+

Motivation

+

[a mechanism to avoid dangling pointers]

+

[a way to break shared_ptr cycles]

+

[weak pointer to this - a technique to obtain a shared_ptr to this from within a + member function]

+

[map<weak_ptr, ...> - a technique to associate arbitrary data with + shared_ptr managed objects]

+

[gameobject/tank example]

+

[cache example]

+

[comparison: weak_ptr vs observer and other approaches]

+

[hard to reinvent, subtle implementation, with many pitfalls]

Synopsis

namespace boost {
 
   template<class T> class weak_ptr {
 
     public:
-      typedef T element_type;
+      typedef T element_type;
 
-      weak_ptr();
-      template<class Y> weak_ptr(shared_ptr<Y> const & r); // never throws
-      ~weak_ptr(); // never throws
+      weak_ptr();
 
-      weak_ptr(weak_ptr const & r); // never throws
-      template<class Y> weak_ptr(weak_ptr<Y> const & r); // never throws
+      template<class Y> weak_ptr(shared_ptr<Y> const & r);
+      weak_ptr(weak_ptr const & r);
+      template<class Y> weak_ptr(weak_ptr<Y> const & r);
 
-      weak_ptr & operator=(weak_ptr const & r); // never throws  
-      template<class Y> weak_ptr & operator=(weak_ptr<Y> const & r); // never throws
-      template<class Y> weak_ptr & operator=(shared_ptr<Y> const & r); // never throws
+      ~weak_ptr();
 
-      void reset();
-      T * get() const; // never throws; deprecated, will disappear
+      weak_ptr & operator=(weak_ptr const & r);
+      template<class Y> weak_ptr & operator=(weak_ptr<Y> const & r);
+      template<class Y> weak_ptr & operator=(shared_ptr<Y> const & r);
 
-      long use_count() const; // never throws
-      bool expired() const; // never throws
+      void reset();
 
-      void swap(weak_ptr<T> & b); // never throws
+      long use_count() const;
+      bool expired() const;
+
+      void swap(weak_ptr<T> & b);
   };
 
   template<class T, class U>
-    bool operator==(weak_ptr<T> const & a, weak_ptr<U> const & b); // never throws
-  template<class T, class U>
-    bool operator!=(weak_ptr<T> const & a, weak_ptr<U> const & b); // never throws
-  template<class T>
-    bool operator<(weak_ptr<T> const & a, weak_ptr<T> const & b); // never throws
-
-  template<class T> void swap(weak_ptr<T> & a, weak_ptr<T> & b); // never throws
+    bool operator<(weak_ptr<T> const & a, weak_ptr<U> const & b);
 
   template<class T>
-    shared_ptr<T> make_shared(weak_ptr<T> const & r); // never throws
+    void swap(weak_ptr<T> & a, weak_ptr<T> & b);
+
+  template<class T>
+    shared_ptr<T> make_shared(weak_ptr<T> const & r);
 
 }
 
@@ -106,49 +120,34 @@ if(shared_ptr<int> r = make_shared(q))

Provides the type of the template parameter T.

-

constructors

-
 weak_ptr();
+

constructors

+
weak_ptr();
-

Effects: Constructs a weak_ptr.

-

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

-

Throws: std::bad_alloc.

-

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

-

Notes: T need not be a complete type. See the smart pointer - common requirements.

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

Effects: Constructs an empty weak_ptr.

+

Postconditions: use count() == 0???.

+

Throws: nothing.

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

Effects: Constructs a weak_ptr, as if by storing a copy of the +

Effects: If r is empty, constructs an empty + weak_ptr; otherwise, constructs a weak_ptr that shares + ownership with r as if by storing a copy of the pointer stored in r.

Throws: nothing.

-

Notes: The use count for all copies is - unchanged. When the last shared_ptr is destroyed, the use count and - stored pointer become 0.

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

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

-

Throws: nothing.

-

Notes: The use count for all copies is - unchanged.

destructor

-
~weak_ptr(); // never throws
+
~weak_ptr();

Effects: Destroys this weak_ptr but has no effect on the object its stored pointer points to.

Throws: nothing.

-

Notes: T need not be a complete type. See the smart pointer - common requirements.

assignment

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

Effects: Equivalent to weak_ptr(r).swap(*this).

Throws: nothing.

@@ -160,75 +159,50 @@ template<class Y> weak_ptr & operator=(share

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

-

get

-
T * get() const; // never throws
-
-

Returns: the stored pointer (0 if all shared_ptr objects for that - pointer are destroyed.)

-

Throws: nothing.

-

Notes: Using get in multithreaded code is dangerous. After the - function returns, the pointed-to object may be destroyed by a different thread, - since the weak_ptr doesn't affect its use_count.

-
-

[get is very error-prone. Even single-threaded code may experience - problems, as the returned pointer may be invalidated at any time, for example, - indirectly by a member function of the pointee.

-

get is deprecated, and it will disappear in a future - release. Do not use it.]

use_count

-
long use_count() const; // never throws
+
long use_count() const;
-

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

+

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

Throws: nothing.

Notes: use_count() is not necessarily efficient. Use only - for debugging and testing purposes, not for production code. T need not - be a complete type. See the smart pointer - common requirements.

+ for debugging and testing purposes, not for production code.

expired

-
bool expired() const; // never throws
+
bool expired() const;

Returns: use_count() == 0.

Throws: nothing.

-

Notes: expired() may be faster than use_count(). - T need not be a complete type. See the smart pointer - common requirements.

+

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

swap

-
void swap(weak_ptr & b); // never throws
+
void swap(weak_ptr & b);

Effects: Exchanges the contents of the two smart pointers.

Throws: nothing.

-

Notes: T need not be a complete type. See the smart pointer - common requirements.

Free Functions

comparison

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

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

-

Throws: nothing.

-

Notes: T need not be a complete type. See the smart pointer - common requirements.

-
-
template<class T>
-  bool operator<(weak_ptr<T> const & a, weak_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] - 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 weak_ptr instances + are equivalent if and only if they share ownership.

Throws: nothing.

Notes: Allows weak_ptr objects to be used as keys in - associative containers. T need not be a complete type. See the smart - pointer common requirements.

+ associative containers.

swap

template<class T>
-  void swap(weak_ptr<T> & a, weak_ptr<T> & b) // never throws
+ void swap(weak_ptr<T> & a, weak_ptr<T> & b)

Effects: Equivalent to a.swap(b).

Throws: nothing.

@@ -237,19 +211,44 @@ template<class T, class U>

make_shared

template<class T>
-  shared_ptr<T> make_shared(weak_ptr<T> & const r) // never throws
+ shared_ptr<T> make_shared(weak_ptr<T> & const r)

Returns: r.expired()? shared_ptr<T>(): shared_ptr<T>(r).

Throws: nothing.

-

[The current implementation of make_shared can propagate - an exception thrown by the shared_ptr default - constructor, so it doesn't meet the stated requirements. In a future - release, this default constructor will not throw.]

+

Frequently Asked Questions

+

Q. Can an object create a weak_ptr to itself in its + constructor?

+

A. No. A weak_ptr can only be created from a shared_ptr, + and at object construction time no shared_ptr to the object + exists yet. Even if you could create a temporary shared_ptr to + this, it would go out of scope at the end of the constructor, and + all weak_ptr instances would instantly expire.

+

The solution is to make the constructor private, and supply a factory function + that returns a shared_ptr:
+

+
+class X
+{
+private:
+
+    X();
+
+public:
+
+    static shared_ptr<X> create()
+    {
+        shared_ptr<X> px(new X);
+        // create weak pointers from px here
+        return px;
+    }
+};
+
+



-

Revised 29 August 2002

+

$Date$

Copyright 1999 Greg Colvin and Beman Dawes. Copyright 2002 Darin Adler. - Copyright 2002 Peter Dimov. Permission to copy, use, modify, sell and + 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.