diff --git a/compatibility.htm b/compatibility.htm new file mode 100644 index 0000000..fb4d0a5 --- /dev/null +++ b/compatibility.htm @@ -0,0 +1,105 @@ + + + + +
+ +The February 2002 change to the Boost smart pointers introduced a number +of changes. Since the previous version of the smart pointers was in use for +a long time, it's useful to have a detailed list of what changed from a library +user's point of view.
+ +Note that for compilers that don't support member templates well enough, +a separate implementation is used that lacks many of the new features and is +more like the old version.
+ +Revised 1 February 2002
+ +Copyright 2002 Darin Adler. +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/enable_shared_from_this.html b/enable_shared_from_this.html new file mode 100644 index 0000000..8ff0a73 --- /dev/null +++ b/enable_shared_from_this.html @@ -0,0 +1,93 @@ + + + +
+ ![]() |
+
+ enable_shared_from_this.hpp+ |
+
+ |
+ The header <boost/enable_shared_from_this.hpp> defines + the class template enable_shared_from_this. It is used as a + base class that allows a shared_ptr to the current + object to be obtained from within a member function. +
+enable_shared_from_this<T> defines two member functions + called shared_from_this that return a shared_ptr<T> + and shared_ptr<T const>, depending on constness, to this.
++class Y: public enable_shared_from_this<Y> +{ +public: + + shared_ptr<Y> f() + { + return shared_from_this(); + } +} + +int main() +{ + shared_ptr<Y> p(new Y); + shared_ptr<Y> q = p->f(); + assert(p == q); + assert(!(p < q || q < p)); // p and q must share ownership +} ++
+namespace boost +{ + +template<class T> class enable_shared_from_this +{ +public: + + shared_ptr<T> shared_from_this(); + shared_ptr<T const> shared_from_this() const; +} + +} ++
+++ Requires: enable_shared_from_this<T> must be an + accessible base class of T. *this must be a subobject + of an instance t of type T . There must exist + at least one shared_ptr instance p that owns + t. +
++ Returns: A shared_ptr<T> instance r that shares + ownership with p. +
++ Postconditions: r.get() == this. +
+
+
+ Copyright © 2002, 2003 by 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.
![]() |
+ Home | +Libraries | +People | +FAQ | +More | +
The smart pointer library includes five smart pointer class templates. Smart + pointers ease the management of memory dynamically allocated with C++ new + expressions. In addition, scoped_ptr can ease the management of memory + dynamically allocated in other ways.
+$Date$
+ + diff --git a/intrusive_ptr.html b/intrusive_ptr.html new file mode 100644 index 0000000..22bbc0c --- /dev/null +++ b/intrusive_ptr.html @@ -0,0 +1,276 @@ + + + +
+ Introduction
+ Synopsis
+ Members
+ Free Functions
+
The intrusive_ptr class template stores a pointer to an object with an + embedded reference count. Every new intrusive_ptr instance increments + the reference count by using an unqualified call to the function intrusive_ptr_add_ref, + passing it the pointer as an argument. Similarly, when an intrusive_ptr + is destroyed, it calls intrusive_ptr_release; this function is + responsible for destroying the object when its reference count drops to zero. + The user is expected to provide suitable definitions of these two functions. On + compilers that support argument-dependent lookup, intrusive_ptr_add_ref + and intrusive_ptr_release should be defined in the namespace + that corresponds to their parameter; otherwise, the definitions need to go in + namespace boost.
+The class template is parameterized on T, the type of the object pointed + to. intrusive_ptr<T> can be implicitly converted to intrusive_ptr<U> + whenever T* can be implicitly converted to U*.
+The main reasons to use intrusive_ptr are:
+As a general rule, if it isn't obvious whether intrusive_ptr better + fits your needs than shared_ptr, try a shared_ptr-based + design first.
+namespace boost { + + template<class T> class intrusive_ptr { + + public: + + typedef T element_type; + + intrusive_ptr(); // never throws + intrusive_ptr(T * p, bool add_ref = true); + + intrusive_ptr(intrusive_ptr const & r); + template<class Y> intrusive_ptr(intrusive_ptr<Y> const & r); + + ~intrusive_ptr(); + + intrusive_ptr & operator=(intrusive_ptr const & r); + template<class Y> intrusive_ptr & operator=(intrusive_ptr<Y> const & r); + template<class Y> intrusive_ptr & operator=(T * r); + + T & operator*() const; // never throws + T * operator->() const; // never throws + T * get() const; // never throws + + operator unspecified-bool-type() const; // never throws + + void swap(intrusive_ptr & b); // never throws + }; + + template<class T, class U> + bool operator==(intrusive_ptr<T> const & a, intrusive_ptr<U> const & b); // never throws + + template<class T, class U> + bool operator!=(intrusive_ptr<T> const & a, intrusive_ptr<U> const & b); // never throws + + template<class T> + bool operator==(intrusive_ptr<T> const & a, T * b); // never throws + + template<class T> + bool operator!=(intrusive_ptr<T> const & a, T * b); // never throws + + template<class T> + bool operator==(T * a, intrusive_ptr<T> const & b); // never throws + + template<class T> + bool operator!=(T * a, intrusive_ptr<T> const & b); // never throws + + template<class T, class U> + bool operator<(intrusive_ptr<T> const & a, intrusive_ptr<U> const & b); // never throws + + template<class T> void swap(intrusive_ptr<T> & a, intrusive_ptr<T> & b); // never throws + + template<class T> T * get_pointer(intrusive_ptr<T> const & p); // never throws + + template<class T, class U> + intrusive_ptr<T> static_pointer_cast(intrusive_ptr<U> const & r); // never throws + + template<class T, class U> + intrusive_ptr<T> dynamic_pointer_cast(intrusive_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, intrusive_ptr<Y> const & p); + +}+
typedef T element_type;+
++Provides the type of the template parameter T.
+
intrusive_ptr(); // never throws+
++Postconditions:
+get() == 0
.Throws: nothing.
+
intrusive_ptr(T * p, bool add_ref = true);+
++Effects:
+if(p != 0 && add_ref) intrusive_ptr_add_ref(p);
.Postconditions:
+get() == p
.
intrusive_ptr(intrusive_ptr const & r); // never throws +template<class Y> intrusive_ptr(intrusive_ptr<Y> const & r); // never throws+
++Effects:
+if(r.get() != 0) intrusive_ptr_add_ref(r.get());
.Postconditions:
+get() == r.get()
.
~intrusive_ptr();+
++Effects:
+if(get() != 0) intrusive_ptr_release(get());
.
intrusive_ptr & operator=(intrusive_ptr const & r); // never throws +template<class Y> intrusive_ptr & operator=(intrusive_ptr<Y> const & r); // never throws +intrusive_ptr & operator=(T * r);+
++Effects: Equivalent to
+intrusive_ptr(r).swap(*this)
.Returns:
+*this
.
T & operator*() const; // never throws+
++Requirements:
+get() != 0
.Returns:
+*get()
.Throws: nothing.
+
T * operator->() const; // never throws+
++Requirements:
+get() != 0
.Returns:
+get()
.Throws: nothing.
+
T * get() const; // never throws+
++Returns: the stored pointer.
+Throws: nothing.
+
operator unspecified-bool-type () const; // never throws+
++Returns: an unspecified value that, when used in boolean contexts, is + equivalent to
+get() != 0
.Throws: nothing.
+Notes: This conversion operator allows intrusive_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, avoiding + many of the implicit conversion pitfalls.
void swap(intrusive_ptr & b); // never throws+
++Effects: Exchanges the contents of the two smart pointers.
+Throws: nothing.
+
template<class T, class U> + bool operator==(intrusive_ptr<T> const & a, intrusive_ptr<U> const & b); // never throws+
++Returns:
+a.get() == b.get()
.Throws: nothing.
+
template<class T, class U> + bool operator!=(intrusive_ptr<T> const & a, intrusive_ptr<U> const & b); // never throws+
++Returns:
+a.get() != b.get()
.Throws: nothing.
+
template<class T> + bool operator==(intrusive_ptr<T> const & a, T * b); // never throws+
++Returns:
+a.get() == b
.Throws: nothing.
+
template<class T> + bool operator!=(intrusive_ptr<T> const & a, T * b); // never throws+
++Returns:
+a.get() != b
.Throws: nothing.
+
template<class T> + bool operator==(T * a, intrusive_ptr<T> const & b); // never throws+
++Returns:
+a == b.get()
.Throws: nothing.
+
template<class T> + bool operator!=(T * a, intrusive_ptr<T> const & b); // never throws+
++Returns:
+a != b.get()
.Throws: nothing.
+
template<class T, class U> + bool operator<(intrusive_ptr<T> const & a, intrusive_ptr<U> const & b); // never throws+
++Returns:
+std::less<T *>()(a.get(), b.get())
.Throws: nothing.
+Notes: Allows intrusive_ptr objects to be used as keys + in associative containers.
+
template<class T> + void swap(intrusive_ptr<T> & a, intrusive_ptr<T> & b); // never throws+
++Effects: Equivalent to
+a.swap(b)
.Throws: nothing.
+Notes: Matches the interface of std::swap. Provided as an aid to + generic programming.
+
template<class T> + T * get_pointer(intrusive_ptr<T> const & p); // never throws+
++Returns:
+p.get()
.Throws: nothing.
+Notes: Provided as an aid to generic programming. Used by + mem_fn.
+
template<class T, class U> + intrusive_ptr<T> static_pointer_cast(intrusive_ptr<U> const & r); // never throws+
++Returns:
+intrusive_ptr<T>(static_cast<T*>(r.get()))
.Throws: nothing.
+
template<class T, class U> + intrusive_ptr<T> dynamic_pointer_cast(intrusive_ptr<U> const & r);+
++Returns:
+intrusive_ptr<T>(dynamic_cast<T*>(r.get()))
.Throws: nothing.
+
template<class E, class T, class Y> + std::basic_ostream<E, T> & operator<< (std::basic_ostream<E, T> & os, intrusive_ptr<Y> const & p);+
++Effects:
+os << p.get();
.Returns:
+os
.
+ $Date$
++ Copyright © 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/msvcspeed.gif b/msvcspeed.gif new file mode 100644 index 0000000..56295ee Binary files /dev/null and b/msvcspeed.gif differ diff --git a/scoped_array.htm b/scoped_array.htm new file mode 100644 index 0000000..9fef424 --- /dev/null +++ b/scoped_array.htm @@ -0,0 +1,111 @@ + + + +The scoped_array class template stores a pointer to a dynamically + allocated array. (Dynamically allocated arrays are allocated with the C++ new[] + expression.) The array pointed to is guaranteed to be deleted, either on + destruction of the scoped_array, or via an explicit reset.
+The scoped_array template is a simple solution for simple needs. It + supplies a basic "resource acquisition is initialization" facility, without + shared-ownership or transfer-of-ownership semantics. Both its name and + enforcement of semantics (by being + noncopyable) signal its intent to retain ownership solely within the + current scope. Because it is + noncopyable, + it is safer than shared_array for pointers which should not be copied.
+Because scoped_array is so simple, in its usual implementation every + operation is as fast as a built-in array pointer and it has no more space + overhead that a built-in array pointer.
+It cannot be used in C++ standard library containers. See + shared_array if scoped_array does not meet your needs.
+It cannot correctly hold a pointer to a single object. See scoped_ptr + for that usage.
+A std::vector is an alternative to a scoped_array that is a bit + heavier duty but far more flexible. A boost::array is an alternative + that does not use dynamic allocation.
+The class template is parameterized on T, the type of the object pointed + to. T must meet the smart pointer + common requirements.
+namespace boost { + + template<class T> class scoped_array : noncopyable { + + public: + typedef T element_type; + + explicit scoped_array(T * p = 0); // never throws + ~scoped_array(); // never throws + + void reset(T * p = 0); // never throws + + T & operator[](std::ptrdiff_t i) const; // never throws + T * get() const; // never throws + + void swap(scoped_array & b); // never throws + }; + + template<class T> void swap(scoped_array<T> & a, scoped_array<T> & b); // never throws + +}+
typedef T element_type;+
Provides the type of the stored pointer.
+explicit scoped_array(T * p = 0); // never throws+
Constructs a scoped_array, storing a copy of p, which must have + been allocated via a C++ new[] expression or be 0. T is not + required be a complete type. See the smart pointer + common requirements.
+~scoped_array(); // never throws+
Deletes the array pointed to by the stored pointer. Note that delete[] on + a pointer with a value of 0 is harmless. The guarantee that this does not throw + exceptions depends on the requirement that the deleted array's objects' + destructors do not throw exceptions. See the smart pointer + common requirements.
+void reset(T * p = 0); // never throws+
+ Deletes the array pointed to by the stored pointer and then stores a copy of p, + which must have been allocated via a C++ new[] expression or be 0. The + guarantee that this does not throw exceptions depends on the requirement that + the deleted array's objects' destructors do not throw exceptions. See the smart + pointer common requirements.
+T & operator[](std::ptrdiff_t i) const; // never throws+
Returns a reference to element i of the array pointed to by the stored + pointer. Behavior is undefined and almost certainly undesirable if the stored + pointer is 0, or if i is less than 0 or is greater than or equal to the + number of elements in the array.
+T * get() const; // never throws+
Returns the stored pointer. T need not be a complete type. See the smart + pointer common requirements.
+void swap(scoped_array & b); // never throws+
Exchanges the contents of the two smart pointers. T need not be a + complete type. See the smart pointer common + requirements.
+template<class T> void swap(scoped_array<T> & a, scoped_array<T> & b); // never throws+
Equivalent to a.swap(b). Matches the interface of std::swap. + Provided as an aid to generic programming.
+Revised 09 January 2003
+Copyright 1999 Greg Colvin and Beman Dawes. Copyright 2002 Darin Adler. + 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.
+ + \ No newline at end of file diff --git a/scoped_ptr.htm b/scoped_ptr.htm new file mode 100644 index 0000000..4a15223 --- /dev/null +++ b/scoped_ptr.htm @@ -0,0 +1,176 @@ + + + +The scoped_ptr class template stores a pointer to a dynamically allocated + object. (Dynamically allocated objects are allocated with the C++ new expression.) + The object pointed to is guaranteed to be deleted, either on destruction of the scoped_ptr, + or via an explicit reset. See the example.
+The scoped_ptr template is a simple solution for simple needs. It + supplies a basic "resource acquisition is initialization" facility, without + shared-ownership or transfer-of-ownership semantics. Both its name and + enforcement of semantics (by being + noncopyable) signal its intent to retain ownership solely within the + current scope. Because it is noncopyable, + it is safer than shared_ptr or std::auto_ptr for pointers which + should not be copied.
+Because scoped_ptr is simple, in its usual implementation every operation + is as fast as for a built-in pointer and it has no more space overhead that a + built-in pointer.
+scoped_ptr cannot be used in C++ Standard Library containers. + Use shared_ptr if you need a smart pointer + that can.
+scoped_ptr cannot correctly hold a pointer to a dynamically + allocated array. See scoped_array for + that usage.
+The class template is parameterized on T, the type of the object pointed + to. T must meet the smart pointer + common requirements.
+namespace boost { + + template<class T> class scoped_ptr : noncopyable { + + public: + typedef T element_type; + + explicit scoped_ptr(T * p = 0); // never throws + ~scoped_ptr(); // never throws + + void reset(T * p = 0); // never throws + + T & operator*() const; // never throws + T * operator->() const; // never throws + T * get() const; // never throws + + void swap(scoped_ptr & b); // never throws + }; + + template<class T> void swap(scoped_ptr<T> & a, scoped_ptr<T> & b); // never throws + +}+
typedef T element_type;+
Provides the type of the stored pointer.
+explicit scoped_ptr(T * p = 0); // never throws+
Constructs a scoped_ptr, storing a copy of p, which must have been + allocated via a C++ new expression or be 0. T is not required be + a complete type. See the smart pointer common + requirements.
+~scoped_ptr(); // never throws+
Destroys the object pointed to by the stored pointer, if any, as if by using delete + this->get().
++ The guarantee that this does not throw exceptions depends on the requirement + that the deleted object's destructor does not throw exceptions. See the smart + pointer common requirements.
+void reset(T * p = 0); // never throws+
+ Deletes the object pointed to by the stored pointer and then stores a copy of + p, which must have been allocated via a C++ new expression or be 0. The + guarantee that this does not throw exceptions depends on the requirement that + the deleted object's destructor does not throw exceptions. See the smart + pointer common requirements.
+T & operator*() const; // never throws+
Returns a reference to the object pointed to by the stored pointer. Behavior is + undefined if the stored pointer is 0.
+T * operator->() const; // never throws+
Returns the stored pointer. Behavior is undefined if the stored pointer is 0.
+T * get() const; // never throws+
Returns the stored pointer. T need not be a complete type. See the smart + pointer common requirements.
+void swap(scoped_ptr & b); // never throws+
Exchanges the contents of the two smart pointers. T need not be a + complete type. See the smart pointer common + requirements.
+template<class T> void swap(scoped_ptr<T> & a, scoped_ptr<T> & b); // never throws+
Equivalent to a.swap(b). Matches the interface of std::swap. + Provided as an aid to generic programming.
+Here's an example that uses scoped_ptr.
+++#include <boost/scoped_ptr.hpp> +#include <iostream> + +struct Shoe { ~Shoe() { std::cout << "Buckle my shoe\n"; } }; + +class MyClass { + boost::scoped_ptr<int> ptr; + public: + MyClass() : ptr(new int) { *ptr = 0; } + int add_one() { return ++*ptr; } +}; + +void main() +{ + boost::scoped_ptr<Shoe> x(new Shoe); + MyClass my_instance; + std::cout << my_instance.add_one() << '\n'; + std::cout << my_instance.add_one() << '\n'; +}+
The example program produces the beginning of a child's nursery rhyme:
+++1 +2 +Buckle my shoe+
The primary reason to use scoped_ptr rather than auto_ptr is to + let readers of your code know that you intend "resource acquisition is + initialization" to be applied only for the current scope, and have no intent to + transfer ownership.
+A secondary reason to use scoped_ptr is to prevent a later maintenance + programmer from adding a function that transfers ownership by returning the auto_ptr, + because the maintenance programmer saw auto_ptr, and assumed ownership + could safely be transferred.
+Think of bool vs int. We all know that under the covers bool + is usually just an int. Indeed, some argued against including bool + in the C++ standard because of that. But by coding bool rather than int, + you tell your readers what your intent is. Same with scoped_ptr; by + using it you are signaling intent.
+It has been suggested that scoped_ptr<T> is equivalent to std::auto_ptr<T> + const. Ed Brey pointed out, however, that reset will not work on + a std::auto_ptr<T> const.
+One common usage of scoped_ptr is to implement a handle/body (also called + pimpl) idiom which avoids exposing the body (implementation) in the header + file.
+The scoped_ptr_example_test.cpp sample + program includes a header file, scoped_ptr_example.hpp, + which uses a scoped_ptr<> to an incomplete type to hide the + implementation. The instantiation of member functions which require a complete + type occurs in the scoped_ptr_example.cpp + implementation file.
+Q. Why doesn't scoped_ptr have a release() member?
+ A. When reading source code, it is valuable to be able to draw
+ conclusions about program behavior based on the types being used. If scoped_ptr
+ had a release() member, it would become possible to transfer ownership of the
+ held pointer, weakening its role as a way of limiting resource lifetime to a
+ given context. Use std::auto_ptr where transfer of ownership
+ is required. (supplied by Dave Abrahams)
Revised + 09 January 2003
+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 + warranty, and with no claim as to its suitability for any purpose.
+ + diff --git a/shared_array.htm b/shared_array.htm new file mode 100644 index 0000000..f4d5f04 --- /dev/null +++ b/shared_array.htm @@ -0,0 +1,180 @@ + + + +The shared_array class template stores a pointer to a dynamically + allocated array. (Dynamically allocated array are allocated with the C++ new[] + expression.) The object pointed to is guaranteed to be deleted when the last shared_array + pointing to it is destroyed or reset.
+Every shared_array 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 shared_array + works with the standard library's associative containers.
+Normally, a shared_array cannot correctly hold a pointer to an object + that has been allocated with the non-array form of new. See + shared_ptr for that usage.
+Because the implementation uses reference counting, cycles of shared_array + instances will not be reclaimed. For example, if main() holds a shared_array + to A, which directly or indirectly holds a shared_array back to A, + A's use count will be 2. Destruction of the original shared_array + will leave A dangling with a use count of 1.
+A shared_ptr to a std::vector is an alternative to a shared_array + that is a bit heavier duty but far more flexible.
+The class template is parameterized on T, the type of the object pointed + to. T must meet the smart pointer + common requirements.
+namespace boost { + + template<class T> class shared_array { + + public: + typedef T element_type; + + explicit shared_array(T * p = 0); + template<class D> shared_array(T * p, D d); + ~shared_array(); // never throws + + shared_array(shared_array const & r); // never throws + + shared_array & operator=(shared_array const & r); // never throws + + void reset(T * p = 0); + template<class D> void reset(T * p, D d); + + T & operator[](std::ptrdiff_t i) const() const; // never throws + T * get() const; // never throws + + bool unique() const; // never throws + long use_count() const; // never throws + + void swap(shared_array<T> & b); // never throws + }; + + template<class T> + bool operator==(shared_array<T> const & a, shared_array<T> const & b); // never throws + template<class T> + bool operator!=(shared_array<T> const & a, shared_array<T> const & b); // never throws + template<class T> + bool operator<(shared_array<T> const & a, shared_array<T> const & b); // never throws + + template<class T> void swap(shared_array<T> & a, shared_array<T> & b); // never throws + +}+
typedef T element_type;+
Provides the type of the stored pointer.
+explicit shared_array(T * p = 0);+
Constructs a shared_array, storing a copy of p, which must be a + pointer to an array that was allocated via a C++ new[] expression or be + 0. Afterwards, the use count is 1 (even if p == 0; see + ~shared_array). The only exception which may be thrown by this + constructor is std::bad_alloc. If an exception is thrown, delete[] p + is called.
+template<class D> shared_array(T * p, D d);+
Constructs a shared_array, storing a copy of p and of d. + Afterwards, the use count is 1. D's copy + constructor and destructor must not throw. When the the time comes to delete + the array pointed to by p, the object d is used in the statement d(p). + Invoking the object d with parameter p in this way must not + throw. The only exception which may be thrown by this constructor is std::bad_alloc. + If an exception is thrown, d(p) is called.
+shared_array(shared_array const & r); // never throws+
Constructs a shared_array, as if by storing a copy of the pointer stored + in r. Afterwards, the use count for all copies + is 1 more than the initial use count.
+~shared_array(); // never throws+
Decrements the use count. Then, if the use count is 0, + deletes the array pointed to by the stored pointer. Note that delete[] on + a pointer with a value of 0 is harmless. T need not be a complete type. + The guarantee that this does not throw exceptions depends on the requirement + that the deleted object's destructor does not throw exceptions. See the smart + pointer common requirements.
+shared_array & operator=(shared_array const & r); // never throws+
Constructs a new shared_array as described above, + then replaces this shared_array with the new one, destroying the + replaced object.
+void reset(T * p = 0);+
Constructs a new shared_array as described above, + then replaces this shared_array with the new one, destroying the + replaced object. The only exception which may be thrown is std::bad_alloc. + If an exception is thrown, delete[] p is called.
+template<class D> void reset(T * p, D d);+
Constructs a new shared_array as described above, + then replaces this shared_array with the new one, destroying the + replaced object. D's copy constructor must not throw. The only exception + which may be thrown is std::bad_alloc. If an exception is thrown, d(p) + is called.
+T & operator[](std::ptrdiff_t i) const; // never throws+
Returns a reference to element i of the array pointed to by the stored + pointer. Behavior is undefined and almost certainly undesirable if the stored + pointer is 0, or if i is less than 0 or is greater than or equal to the + number of elements in the array.
+T * get() const; // never throws+
Returns the stored pointer. T need not be a complete type. See the smart + pointer common requirements.
+bool unique() const; // never throws+
Returns true if no other shared_array is sharing ownership of the stored + pointer, false otherwise. T need not be a complete type. See the smart + pointer common requirements.
+long use_count() const; // never throws+
Returns the number of shared_array objects sharing ownership of the + stored pointer. T need not be a complete type. See the smart pointer + common requirements.
+Because use_count is not necessarily efficient to implement for + implementations of shared_array that do not use an explicit reference + count, it might be removed from some future version. Thus it should be used for + debugging purposes only, and not production code.
+void swap(shared_ptr & b); // never throws+
Exchanges the contents of the two smart pointers. T need not be a + complete type. See the smart pointer common + requirements.
+template<class T> + bool operator==(shared_array<T> const & a, shared_array<T> const & b); // never throws +template<class T> + bool operator!=(shared_array<T> const & a, shared_array<T> const & b); // never throws +template<class T> + bool operator<(shared_array<T> const & a, shared_array<T> const & b); // never throws+
Compares the stored pointers of the two smart pointers. T need not be a + complete type. See the smart pointer common + requirements.
+The operator< overload is provided to define an ordering so that shared_array + objects can be used in associative containers such as std::map. The + implementation uses std::less<T *> to perform the comparison. This + ensures that the comparison is handled correctly, since the standard mandates + that relational operations on pointers are unspecified (5.9 [expr.rel] + paragraph 2) but std::less<> on pointers is well-defined (20.3.3 + [lib.comparisons] paragraph 8).
+template<class T> + void swap(shared_array<T> & a, shared_array<T> & b) // never throws+
Equivalent to a.swap(b). Matches the interface of std::swap. + Provided as an aid to generic programming.
+Revised + + 09 January 2003
+Copyright 1999 Greg Colvin and Beman Dawes. Copyright 2002 Darin Adler. + 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/shared_ptr.htm b/shared_ptr.htm new file mode 100644 index 0000000..809ceb3 --- /dev/null +++ b/shared_ptr.htm @@ -0,0 +1,681 @@ + + + +Introduction
+ Best Practices
+ Synopsis
+ Members
+ Free Functions
+ Example
+ Handle/Body Idiom
+ Thread Safety
+ Frequently Asked Questions
+ Smart Pointer Timings
+ Programming Techniques
The shared_ptr class template stores a pointer to a dynamically allocated + object, typically with a C++ new-expression . The object pointed to is + guaranteed to be deleted when the last shared_ptr pointing to it is + destroyed or reset. See the example.
+Every shared_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 shared_ptr + works with the standard library's associative containers.
+Normally, a shared_ptr cannot correctly hold a pointer to a dynamically + allocated array. See shared_array for + that usage.
+Because the implementation uses reference counting, cycles of shared_ptr instances + will not be reclaimed. For example, if main() holds a shared_ptr to + A, which directly or indirectly holds a shared_ptr back to A, + A's use count will be 2. Destruction of the original shared_ptr will + leave A dangling with a use count of 1. Use weak_ptr + to "break cycles."
+The class template is parameterized on T, the type of the object pointed + to. shared_ptr and most of its member functions place no + requirements on T; it is allowed to be an incomplete type, or + void. Member functions that do place additional requirements (constructors, + reset) are explicitly documented below.
+shared_ptr<T> can be implicitly converted to shared_ptr<U> + whenever T* can be implicitly converted to U*. + In particular, shared_ptr<T> is implicitly convertible + to shared_ptr<T const>, to shared_ptr<U> + where U is an accessible base of T, and to + shared_ptr<void>.
+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 (also + here) of the issue for more information.
+namespace boost { + + class bad_weak_ptr: public std::exception; + + template<class T> class weak_ptr; + + template<class T> class shared_ptr { + + public: + + typedef T element_type; + + 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(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(); // never throws + template<class Y> void reset(Y * p); + template<class Y, class D> void reset(Y * p, D d); + + T & operator*() const; // never throws + T * operator->() const; // never throws + T * get() const; // never throws + + bool unique() const; // never throws + long use_count() 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, 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> static_pointer_cast(shared_ptr<U> const & r); // never throws + + template<class T, class U> + 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); + + template<class D, class T> + D * get_deleter(shared_ptr<T> 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 + 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 + "hack".]
+typedef T element_type;+
++Provides the type of the template parameter T.
+
shared_ptr(); // never throws+
++Effects: Constructs an empty shared_ptr. Empty + shared_ptr objects have an unspecified use_count.
+Postconditions:
+get() == 0
.Throws: nothing.
+
[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. 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 + be used as a shorthand for shared_ptr<T>().]
+template<class Y> explicit shared_ptr(Y * p);+
++Requirements: p must be convertible to T *. Y + must be a complete type. The expression
+delete p
must be + well-formed, must not invoke undefined behavior, and must not throw exceptions. +Effects: Constructs a shared_ptr that owns the pointer p.
+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 + allocated via a C++ new expression or be 0. The postcondition that + 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.
+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 + must be CopyConstructible. The copy constructor and destructor + of D must not throw. The expression
+d(p)
must be + well-formed, must not invoke undefined behavior, and must not throw exceptions. +Effects: Constructs a shared_ptr that owns the pointer + p and the deleter d.
+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 + as an argument.
+
[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, 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.
+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: If r is empty, constructs an empty shared_ptr; + otherwise, constructs a shared_ptr that shares ownership with r.
+Postconditions:
+get() == r.get() && use_count() == + r.use_count()
.Throws: nothing.
+
template<class Y> explicit shared_ptr(weak_ptr<Y> const & r);+
++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.
+Postconditions:
+use_count() == r.use_count()
.Throws: bad_weak_ptr when
+r.use_count() == 0
.Exception safety: If an exception is thrown, the constructor has no + effect.
+
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() == 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; an rvalue reference + would solve this problem, too.]
+~shared_ptr(); // never throws+
++Effects:
++
+- + If *this is empty, or shares ownership with + another shared_ptr instance (
use_count() > 1
), + there are no side effects. +- + Otherwise, if *this owns a pointer p + and a deleter d,
d(p)
+ is called. +- + Otherwise, *this owns a pointer p, + and
delete p
is called.Throws: nothing.
+
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);+
++Effects: Equivalent to
+shared_ptr(r).swap(*this)
.Returns:
+*this
.Notes: The use count updates caused by the temporary object construction + 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<void> q(p); +p = p; +q = p; ++both assignments may be no-ops.
+
[Some experts consider the note to be redundant, as it appears to essentially + mirror the "as if" rule. However, experience suggests that when C++ code is + used to describe effects, it is often misinterpreted as required + implementation. In addition, it is not entirely clear whether the "as if" rule + actually applies here, so it's better to be explicit about the possible + optimizations.]
+void reset(); // never throws+
++Effects: Equivalent to
+shared_ptr().swap(*this)
.
template<class Y> void reset(Y * p);+
++Effects: Equivalent to
+shared_ptr(p).swap(*this)
.
template<class Y, class D> void reset(Y * p, D d);+
++Effects: Equivalent to
+shared_ptr(p, d).swap(*this)
.
T & operator*() const; // never throws+
++Requirements: The stored pointer must not be 0.
+Returns: a reference to the object pointed to by the stored pointer.
+Throws: nothing.
+
T * operator->() const; // never throws+
++Requirements: The stored pointer must not be 0.
+Returns: the stored pointer.
+Throws: nothing.
+
T * get() const; // never throws+
++Returns: the stored pointer.
+Throws: nothing.
+
bool unique() const; // never throws+
++Returns:
+use_count() == 1
.Throws: nothing.
+Notes:
+unique()
may be faster thanuse_count()
. + If you are usingunique()
to implement copy on write, do not rely + on a specific value when the stored pointer is zero.
long use_count() const; // never throws+
++Returns: the number of shared_ptr objects, *this included, + 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.
operator unspecified-bool-type () const; // never throws+
++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, 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 dynamic_pointer_cast + or weak_ptr::lock.]
+void swap(shared_ptr & b); // never throws+
++Effects: Exchanges the contents of the two smart pointers.
+Throws: nothing.
+
template<class T, class U> + bool operator==(shared_ptr<T> const & a, shared_ptr<U> const & b); // never throws+
++Returns:
+a.get() == b.get()
.Throws: nothing.
+
template<class T, class U> + bool operator!=(shared_ptr<T> const & a, shared_ptr<U> const & b); // never throws+
++Returns:
+a.get() != b.get()
.Throws: nothing.
+
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; +- + 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.
+
[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. Composite objects, like std::pair, + also implement their operator< in terms of their contained + subobjects' operator<.
+The rest of the comparison operators are omitted by design.]
+template<class T> + void swap(shared_ptr<T> & a, shared_ptr<T> & b); // never throws+
++Effects: Equivalent to
+a.swap(b)
.Throws: nothing.
+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.]
+template<class T> + T * get_pointer(shared_ptr<T> const & p); // never throws+
++Returns:
+p.get()
.Throws: nothing.
+Notes: Provided as an aid to generic programming. Used by + mem_fn.
+
template<class T, class U> + 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: 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.
+
template<class T, class U> + 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.Returns:
++
+- + 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; +- + 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.
+
template<class E, class T, class Y> + std::basic_ostream<E, T> & operator<< (std::basic_ostream<E, T> & os, shared_ptr<Y> const & p);+
++Effects:
+os << p.get();
.Returns: os.
+
template<class D, class T> + D * get_deleter(shared_ptr<T> const & p);+
++Returns: If *this owns a deleter d + of type (cv-unqualified) D, returns
+&d
; + otherwise returns 0.
See shared_ptr_example.cpp for a + complete example program. The program builds a std::vector and std::set + of shared_ptr objects.
+Note that after the containers have been populated, some of the shared_ptr + objects will have a use count of 1 rather than a use count of 2, since the set + is a std::set rather than a std::multiset, and thus does not + contain duplicate entries. Furthermore, the use count may be even higher at + various times while push_back and insert container operations are + performed. More complicated yet, the container operations may throw exceptions + under a variety of circumstances. Getting the memory management and exception + handling in this example right without a smart pointer would be a nightmare.
+One common usage of shared_ptr is to implement a handle/body (also called + pimpl) idiom which avoids exposing the body (implementation) in the header + file.
+The shared_ptr_example2_test.cpp + sample program includes a header file, shared_ptr_example2.hpp, + which uses a shared_ptr<> to an incomplete type to hide the + implementation. The instantiation of member functions which require a complete + type occurs in the shared_ptr_example2.cpp + 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.
+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.
+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?
+
+ 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.)
+
Q. Why doesn't shared_ptr have template parameters supplying + 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.)
+
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?
+
+ 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()?
+
+ 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?
+
+ 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?
++ A. shared_ptr cannot give away ownership unless it's unique() + 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 + +int * p = a.release(); + +// Who owns p now? b will still call delete on it in its destructor.+
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".
+
+
+ $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 + warranty, and with no claim as to its suitability for any purpose.
+ + diff --git a/smart_ptr.htm b/smart_ptr.htm new file mode 100644 index 0000000..9327c9f --- /dev/null +++ b/smart_ptr.htm @@ -0,0 +1,186 @@ + + + +Introduction
+ Common Requirements
+ Exception Safety
+ Exception-specifications
+ History and Acknowledgements
+ References
Smart pointers are objects which store pointers to dynamically allocated (heap) + objects. They behave much like built-in C++ pointers except that they + automatically delete the object pointed to at the appropriate time. Smart + pointers are particularly useful in the face of exceptions as they ensure + proper destruction of dynamically allocated objects. They can also be used to + keep track of dynamically allocated objects shared by multiple owners.
+Conceptually, smart pointers are seen as owning the object pointed to, and thus + responsible for deletion of the object when it is no longer needed.
+The smart pointer library provides five smart pointer class templates:
+scoped_ptr | +<boost/scoped_ptr.hpp> | +Simple sole ownership of single objects. Noncopyable. | +
scoped_array | +<boost/scoped_array.hpp> | +Simple sole ownership of arrays. Noncopyable. | +
shared_ptr | +<boost/shared_ptr.hpp> | +Object ownership shared among multiple pointers | +
shared_array | +<boost/shared_array.hpp> | +Array ownership shared among multiple pointers. | +
weak_ptr | +<boost/weak_ptr.hpp> | +Non-owning observers of an object owned by shared_ptr. | +
intrusive_ptr | +<boost/intrusive_ptr.hpp> | +Shared ownership of objects with an embedded reference count. | +
These templates are designed to complement the std::auto_ptr template.
+They are examples of the "resource acquisition is initialization" idiom + described in Bjarne Stroustrup's "The C++ Programming Language", 3rd edition, + Section 14.4, Resource Management.
+A test program, smart_ptr_test.cpp, is + provided to verify correct operation.
+A page on compatibility with older versions of + the Boost smart pointer library describes some of the changes since earlier + versions of the smart pointer implementation.
+A page on smart pointer timings will be of interest + to those curious about performance issues.
+A page on smart pointer programming techniques lists
+ some advanced applications of shared_ptr
and weak_ptr
.
These smart pointer class templates have a template parameter, T, which + specifies the type of the object pointed to by the smart pointer. The behavior + of the smart pointer templates is undefined if the destructor or operator delete + for objects of type T throw exceptions.
+T may be an incomplete type at the point of smart pointer declaration. + Unless otherwise specified, it is required that T be a complete type at + points of smart pointer instantiation. Implementations are required to diagnose + (treat as an error) all violations of this requirement, including deletion of + an incomplete type. See the description of the + checked_delete function template.
+Note that shared_ptr does not have this restriction, as most of + its member functions do not require T to be a complete type.
+The requirements on T are carefully crafted to maximize safety yet allow + handle-body (also called pimpl) and similar idioms. In these idioms a smart + pointer may appear in translation units where T is an incomplete type. + This separates interface from implementation and hides implementation from + translation units which merely use the interface. Examples described in the + documentation for specific smart pointers illustrate use of smart pointers in + these idioms.
+Note that scoped_ptr requires that T be a complete type at + destruction time, but shared_ptr does not.
+Several functions in these smart pointer classes are specified as having "no + effect" or "no effect except such-and-such" if an exception is thrown. This + means that when an exception is thrown by an object of one of these classes, + the entire program state remains the same as it was prior to the function call + which resulted in the exception being thrown. This amounts to a guarantee that + there are no detectable side effects. Other functions never throw exceptions. + The only exception ever thrown by functions which do throw (assuming T meets + the common requirements) is std::bad_alloc, + and that is thrown only by functions which are explicitly documented as + possibly throwing std::bad_alloc.
+Exception-specifications are not used; see + exception-specification rationale.
+All the smart pointer templates contain member functions which can never throw
+ exceptions, because they neither throw exceptions themselves nor call other
+ functions which may throw exceptions. These members are indicated by a comment:
+ // never throws
.
+
Functions which destroy objects of the pointed to type are prohibited from + throwing exceptions by the common requirements.
+January 2002. Peter Dimov reworked all four classes, adding features, fixing + bugs, and splitting them into four separate headers, and added weak_ptr. + See the compatibility page for a summary of the + changes.
+May 2001. Vladimir Prus suggested requiring a complete type on destruction. + Refinement evolved in discussions including Dave Abrahams, Greg Colvin, Beman + Dawes, Rainer Deyke, Peter Dimov, John Maddock, Vladimir Prus, Shankar Sai, and + others.
+November 1999. Darin Adler provided operator ==, operator !=, and std::swap + and std::less specializations for shared types.
+September 1999. Luis Coelho provided shared_ptr::swap and shared_array::swap
+May 1999. In April and May, 1999, Valentin Bonnard and David Abrahams made a + number of suggestions resulting in numerous improvements.
+October 1998. Beman Dawes proposed reviving the original semantics under the + names safe_ptr and counted_ptr, meeting of Per Andersson, Matt + Austern, Greg Colvin, Sean Corfield, Pete Becker, Nico Josuttis, Dietmar Kühl, + Nathan Myers, Chichiang Wan and Judy Ward. During the discussion, the four new + class names were finalized, it was decided that there was no need to exactly + follow the std::auto_ptr interface, and various function signatures and + semantics were finalized.
+Over the next three months, several implementations were considered for shared_ptr, + and discussed on the boost.org mailing list. + The implementation questions revolved around the reference count which must be + kept, either attached to the pointed to object, or detached elsewhere. Each of + those variants have themselves two major variants: +
Each implementation technique has advantages and disadvantages. We went so far + as to run various timings of the direct and indirect approaches, and found that + at least on Intel Pentium chips there was very little measurable difference. + Kevlin Henney provided a paper he wrote on "Counted Body Techniques." Dietmar + Kühl suggested an elegant partial template specialization technique to allow + users to choose which implementation they preferred, and that was also + experimented with.
+But Greg Colvin and Jerry Schwarz argued that "parameterization will discourage + users", and in the end we choose to supply only the direct implementation.
+Summer, 1994. Greg Colvin proposed to the C++ Standards Committee classes named auto_ptr + and counted_ptr which were very similar to what we now call scoped_ptr + and shared_ptr. [Col-94] In one of the very few + cases where the Library Working Group's recommendations were not followed by + the full committee, counted_ptr was rejected and surprising + transfer-of-ownership semantics were added to auto_ptr.
+[Col-94] Gregory Colvin, + Exception Safe Smart Pointers, C++ committee document 94-168/N0555, + July, 1994.
+[E&D-94] John R. Ellis & David L. Detlefs, + Safe, Efficient Garbage Collection for C++, Usenix Proceedings, + February, 1994. This paper includes an extensive discussion of weak pointers + and an extensive bibliography.
+$Date$
+Copyright 1999 Greg Colvin and Beman Dawes. Copyright 2002 Darin Adler. + 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/smarttest.zip b/smarttest.zip new file mode 100644 index 0000000..2464b10 Binary files /dev/null and b/smarttest.zip differ diff --git a/smarttests.htm b/smarttests.htm new file mode 100644 index 0000000..04a4381 --- /dev/null +++ b/smarttests.htm @@ -0,0 +1,543 @@ + + + + + +In late January 2000, Mark Borgerding put forward a suggestion to boost for + a new design of smart pointer whereby an intrusive doubly linked list is used + to join together all instances of smart pointers sharing a given raw pointer. + This allowed avoidance of the costly heap allocation of a reference count that + occurred in the initial construction of the then current version of boost::shared_ptr. + Of course, nothing is for free and the benefit here was gained at the expense + of increased size and more costly copy operations. A debate ensued on the boost + mailing list and the tests which this page describes were performed to provide + a guide for current and future investigations into smart pointer implementation + strategies.
+Thanks are due to Dave Abrahams, +Gavin Collings, +Greg Colvin and +Beman Dawes + for test code and trial implementations, the final version of which can be found + in .zip format here.
+Two tests were run: the first aimed to obtain timings for two basic individual + operations:
+The second attempted to gain more insight into normal usage by timing the fill + and sort algorithms for vectors and lists filled with the various smart pointers.
+Five smart pointer implementation strategies were tested:
+on two compilers:
+Additionally, generated pointer sizes (taking into account struct alignment) + were compared, as were generated code sizes for MSVC mainly by manual inspection + of generated assembly code - a necessity due to function inlining.
+All tests were run on a PII-200 running Windows NT version 4.0
+The following graphs show the overall time in nanoseconds to acquire a pointer + (default construction) perform n amortized copy operations on it and finally + release it. The initial allocation time for the contained pointer is not included, + although the time for it's deallocation is. The contained pointer pointed to + a trivial class, but for the inclusion of an intrusive reference count for the + benefit of the intrusive counted shared pointer. A dumb pointer (i.e. a smart + pointer that simply acquires and releases its contained pointer with no extra + overhead) and a raw pointer were also included for comparison.
++ | + | + |
+ | ![]() |
+ + |
+ | + | + |
+ | ![]() |
+ + |
+ | + | + |
+
Fitting straight lines to the above plots gives the following figures for initialization + and amortized copy operation for the two compilers (times in nanoseconds, errors + at two standard deviations) : -
++
+ + | +
+ initialization
+ |
+ copy operation | +
---|---|---|
+ simple counted
+ |
+ 3000 +/- 170 | +104 +/- 31 | +
+ special counted
+ |
+ 1330 +/- 50 | +85 +/- 9 | +
+ intrusive
+ |
+ 1000 +/- 20 | +71 +/- 3 | +
linked | +970 +/- 60 | +136 +/- 10 | +
cyclic | +1290 +/- 70 | +112 +/- 12 | +
dumb | +1020 +/- 20 | +10 +/- 4 | +
+ raw
+ |
+ 1038 +/- 30 | +10 +/- 5 | +
+ + | +
+ initialization
+ |
+ copy operation | +
---|---|---|
+ simple counted
+ |
+ 4620 +/- 150 | +301 +/- 28 | +
+ special counted
+ |
+ 1990 +/- 40 | +264 +/- 7 | +
+ intrusive
+ |
+ 1590 +/- 70 | +181 +/- 12 | +
linked | +1470 +/- 140 | +345 +/- 26 | +
cyclic | +2180 +/- 100 | +330 +/- 18 | +
dumb | +1590 +/- 70 | +74 +/- 12 | +
+ raw
+ |
+ 1430 +/- 60 | +27 +/- 11 | +
Note that the above times include a certain amount of loop overhead etc. for + each operation. An estimate of the pure smart pointer operation time 'overhead' + can be obtained by subtracting the dumb or raw figure from the smart pointer + time of interest.
+The test involved iterating a loop which creates raw pointers. These were then + shared among a varying number (set size) of smart pointers. A range of set sizes + was used and then a line fitted to get a linear relation with number of initializations + and copy-operations. A spreadsheet was used for the line fit, and to produce + the performance graphs above.
+To gain some insight in to operation within real life programs, this test was + devised. Smart pointers were used to fill standard containers which were then + sorted.
+In this case, the contained pointer pointed to a class which initializes a + private data member to a random value in its default constructor. This value + is used subsequently for the sort comparison test. The class also contains an + intrusive reference count for the benefit of the intrusive counted pointer.
+All times are in seconds for 300,000 contained pointers.
++ | vector | +list | +||
---|---|---|---|---|
+ + | +
+ fill
+ |
+ sort | +fill | +sort | +
+ simple counted
+ |
+ 46.54 | +2.44 | +47.09 | +3.22 | +
+ special counted
+ |
+ 14.02 | +2.83 | +7.28 | +3.21 | +
+ intrusive
+ |
+ 12.15 | +1.91 | +7.99 | +3.08 | +
linked | +12.46 | +2.32 | +8.14 | +3.27 | +
cyclic | +22.60 | +3.19 | +1.63 | +3.18 | +
+ raw
+ |
+ 11.81 | +0.24 | +27.51 | +0.77 | +
+
+ | vector | +list | +||
---|---|---|---|---|
+ + | +
+ fill
+ |
+ sort | +fill | +sort | +
+ simple counted
+ |
+ 1.83 | +2.37 | +1.86 | +4.85 | +
+ special counted
+ |
+ 1.04 | +2.35 | +1.38 | +4.58 | +
+ intrusive
+ |
+ 1.04 | +1.84 | +1.16 | +4.29 | +
linked | +1.08 | +2.00 | +1.21 | +4.33 | +
cyclic | +1.38 | +2.84 | +1.47 | +4.73 | +
+ raw
+ |
+ 0.67 | +0.28 | +1.24 | +1.81 | +
+
The following code sizes were determined by inspection of generated code for + MSVC only. Sizes are given in the form N / M / I where:
++
+ + | +
+ ptr()
+ |
+ ptr(p) | +ptr(ptr) | +op=() | +
+ ~ptr()
+ |
+
---|---|---|---|---|---|
+ simple counted
+ |
+ 38/110/O | +38/110/O | +9/23/I | +22/57/I | +17/40/I | +
+ special counted
+ |
+ 50/141/O | +50/141/O | +9/23/I | +23/64/I | +13/38/I | +
+ intrusive
+ |
+ 1/2/I | +3/6/I | +3/6/I | +6/11/I | +6/11/I | +
+ linked
+ |
+ 5/19/I | +5/15/I | +10/30/I | +27/59/I | +14/38/I | +
During the code inspection, a couple of minor points were noticed: -
+The following smart pointer sizes were obtained in bytes
++ + | +
+ MSVC
+ |
+
+ GCC
+ |
+
---|---|---|
+ simple counted
+ |
+
+ 8
+ |
+
+ 8
+ |
+
+ special counted
+ |
+
+ 8
+ |
+
+ 12
+ |
+
+ intrusive
+ |
+
+ 4
+ |
+
+ 4
+ |
+
+ linked
+ |
+
+ 12
+ |
+
+ 12
+ |
+
+ cyclic
+ |
+
+ 8
+ |
+
+ 8
+ |
+
The timing results mainly speak for themselves: clearly an intrusive pointer + outperforms all others and a simple heap based counted pointer has poor performance + relative to other implementations. The selection of an optimal non-intrusive + smart pointer implementation is more application dependent, however. Where small + numbers of copies are expected, it is likely that the linked implementation + will be favoured. Conversely, for larger numbers of copies a counted pointer + with some type of special purpose allocator looks like a win. Other factors + to bear in mind are: -
+Revised 19 August 2001 +
+© Copyright Gavin Collings 2000. 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/sp_techniques.html b/sp_techniques.html new file mode 100644 index 0000000..826195f --- /dev/null +++ b/sp_techniques.html @@ -0,0 +1,760 @@ + + + +Using incomplete classes for implementation hiding
+ The "Pimpl" idiom
+ Using abstract classes for implementation hiding
+ Preventing delete px.get()
+ Using a shared_ptr
to hold a pointer to an array
+ Encapsulating allocation details, wrapping factory
+ functions
+ Using a shared_ptr
to hold a pointer to a statically
+ allocated object
+ Using a shared_ptr
to hold a pointer to a COM object
+ Using a shared_ptr
to hold a pointer to an object
+ with an embedded reference count
+ Using a shared_ptr
to hold another shared
+ ownership smart pointer
+ Obtaining a shared_ptr
from a raw pointer
+ Obtaining a shared_ptr
(weak_ptr
)
+ to this
in a constructor
+ Obtaining a shared_ptr
to this
+ Using shared_ptr
as a smart counted handle
+ Using shared_ptr
to execute code on block
+ exit
+ Using shared_ptr<void>
to hold an arbitrary
+ object
+ Associating arbitrary data with heterogeneous shared_ptr
+ instances
+ Using shared_ptr
as a CopyConstructible mutex lock
+ Using shared_ptr
to wrap member function calls
+ Delayed deallocation
+ Weak pointers to objects not managed by a shared_ptr
+
A proven technique (that works in C, too) for separating interface from + implementation is to use a pointer to an incomplete class as an opaque handle:
+class FILE; + +FILE * fopen(char const * name, char const * mode); +void fread(FILE * f, void * data, size_t size); +void fclose(FILE * f); ++
It is possible to express the above interface using shared_ptr
,
+ eliminating the need to manually call fclose
:
class FILE; + +shared_ptr<FILE> fopen(char const * name, char const * mode); +void fread(shared_ptr<FILE> f, void * data, size_t size); ++
This technique relies on shared_ptr
's ability to execute a custom
+ deleter, eliminating the explicit call to fclose
, and on the fact
+ that shared_ptr<X>
can be copied and destroyed when X
+ is incomplete.
A C++ specific variation of the incomplete class pattern is the "Pimpl" idiom.
+ The incomplete class is not exposed to the user; it is hidden behind a
+ forwarding facade. shared_ptr
can be used to implement a "Pimpl":
// file.hpp: + +class file +{ +private: + + class impl; + shared_ptr<impl> pimpl_; + +public: + + file(char const * name, char const * mode); + + // compiler generated members are fine and useful + + void read(void * data, size_t size); +}; ++
// file.cpp: + +#include "file.hpp" + +class file::impl +{ +private: + + impl(impl const &); + impl & operator=(impl const &); + + // private data + +public: + + impl(char const * name, char const * mode) { ... } + ~impl() { ... } + void read(void * data, size_t size) { ... } +}; + +file::file(char const * name, char const * mode): pimpl_(new impl(name, mode)) +{ +} + +void file::read(void * data, size_t size) +{ + pimpl_->read(data, size); +} ++
The key thing to note here is that the compiler-generated copy constructor,
+ assignment operator, and destructor all have a sensible meaning. As a result,
+ file
is CopyConstructible
and Assignable
,
+ allowing its use in standard containers.
Another widely used C++ idiom for separating inteface and implementation is to
+ use abstract base classes and factory functions. The abstract classes are
+ sometimes called "interfaces" and the pattern is known as "interface-based
+ programming". Again, shared_ptr
can be used as the return type of
+ the factory functions:
// X.hpp: + +class X +{ +public: + + virtual void f() = 0; + virtual void g() = 0; + +protected: + + ~X() {} +}; + +shared_ptr<X> createX(); ++
-- X.cpp: + +class X_impl: public X +{ +private: + + X_impl(X_impl const &); + X_impl & operator=(X_impl const &); + +public: + + virtual void f() + { + // ... + } + + virtual void g() + { + // ... + } +}; + +shared_ptr<X> createX() +{ + shared_ptr<X> px(new X_impl); + return px; +} ++
A key property of shared_ptr is that the allocation, construction, deallocation,
+ and destruction details are captured at the point of construction, inside the
+ factory function. Note the protected and nonvirtual destructor in the example
+ above. The client code cannot, and does not need to, delete a pointer to X
;
+ the shared_ptr<X>
instance returned from createX
+ will correctly call ~X_impl
.
delete px.get()
It is often desirable to prevent client code from deleting a pointer that is
+ being managed by shared_ptr
. The previous technique showed one
+ possible approach, using a protected destructor. Another alternative is to use
+ a private deleter:
class X +{ +private: + + ~X(); + + class deleter; + friend class deleter; + + class deleter + { + public: + + void operator()(X * p) { delete p; } + }; + +public: + + static shared_ptr<X> create() + { + shared_ptr<X> px(new X, X::deleter()); + return px; + } +}; ++
shared_ptr
to hold a pointer to an arrayA shared_ptr
can be used to hold a pointer to an array allocated
+ with new[]
:
shared_ptr<X> px(new X[1], checked_array_deleter<X>()); ++
Note, however, that shared_array
is
+ often preferable, if this is an option. It has an array-specific interface,
+ without operator*
and operator->
, and does not
+ allow pointer conversions.
shared_ptr
can be used in creating C++ wrappers over existing C
+ style library interfaces that return raw pointers from their factory functions
+ to encapsulate allocation details. As an example, consider this interface,
+ where CreateX
might allocate X
from its own private
+ heap, ~X
may be inaccessible, or X
may be incomplete:
X * CreateX(); +void DestroyX(X *); ++
The only way to reliably destroy a pointer returned by CreateX
is
+ to call DestroyX
.
Here is how a shared_ptr
-based wrapper may look like:
shared_ptr<X> createX() +{ + shared_ptr<X> px(CreateX(), DestroyX); + return px; +} ++
Client code that calls createX
still does not need to know how the
+ object has been allocated, but now the destruction is automatic.
shared_ptr
to hold a pointer to a statically
+ allocated objectSometimes it is desirable to create a shared_ptr
to an already
+ existing object, so that the shared_ptr
does not attempt to
+ destroy the object when there are no more references left. As an example, the
+ factory function:
shared_ptr<X> createX(); ++
in certain situations may need to return a pointer to a statically allocated X
+ instance.
The solution is to use a custom deleter that does nothing:
+struct null_deleter +{ + void operator()(void const *) const + { + } +}; + +static X x; + +shared_ptr<X> createX() +{ + shared_ptr<X> px(&x, null_deleter()); + return px; +} ++
The same technique works for any object known to outlive the pointer.
+shared_ptr
to hold a pointer to a COM ObjectBackground: COM objects have an embedded reference count and two member
+ functions that manipulate it. AddRef()
increments the count. Release()
+ decrements the count and destroys itself when the count drops to zero.
It is possible to hold a pointer to a COM object in a shared_ptr
:
shared_ptr<IWhatever> make_shared_from_COM(IWhatever * p) +{ + p->AddRef(); + shared_ptr<IWhatever> pw(p, mem_fn(&IWhatever::Release)); + return pw; +} ++
Note, however, that shared_ptr
copies created from pw
will
+ not "register" in the embedded count of the COM object; they will share the
+ single reference created in make_shared_from_COM
. Weak pointers
+ created from pw
will be invalidated when the last shared_ptr
+ is destroyed, regardless of whether the COM object itself is still alive.
shared_ptr
to hold a pointer to an object
+ with an embedded reference countThis is a generalization of the above technique. The example assumes that the
+ object implements the two functions required by intrusive_ptr
,
+ intrusive_ptr_add_ref
and intrusive_ptr_release
:
template<class T> struct intrusive_deleter +{ + void operator()(T * p) + { + if(p) intrusive_ptr_release(p); + } +}; + +shared_ptr<X> make_shared_from_intrusive(X * p) +{ + if(p) intrusive_ptr_add_ref(p); + shared_ptr<X> px(p, intrusive_deleter<X>()); + return px; +} ++
shared_ptr
to hold another shared
+ ownership smart pointerOne of the design goals of shared_ptr
is to be used in library
+ interfaces. It is possible to encounter a situation where a library takes a shared_ptr
+ argument, but the object at hand is being managed by a different reference
+ counted or linked smart pointer.
It is possible to exploit shared_ptr
's custom deleter feature to
+ wrap this existing smart pointer behind a shared_ptr
facade:
template<class P> struct smart_pointer_deleter +{ +private: + + P p_; + +public: + + smart_pointer_deleter(P const & p): p_(p) + { + } + + void operator()(void const *) + { + p_.reset(); + } + + P const & get() const + { + return p_; + } +}; + +shared_ptr<X> make_shared_from_another(another_ptr<X> qx) +{ + shared_ptr<X> px(qx.get(), smart_pointer_deleter< another_ptr<X> >(qx)); + return px; +} ++
One subtle point is that deleters are not allowed to throw exceptions, and the
+ above example as written assumes that p_.reset()
doesn't throw. If
+ this is not the case, p_.reset()
should be wrapped in a try {}
+ catch(...) {}
block that ignores exceptions. In the (usually
+ unlikely) event when an exception is thrown and ignored, p_
will
+ be released when the lifetime of the deleter ends. This happens when all
+ references, including weak pointers, are destroyed or reset.
Another twist is that it is possible, given the above shared_ptr
instance,
+ to recover the original smart pointer, using
+ get_deleter
:
void extract_another_from_shared(shared_ptr<X> px) +{ + typedef smart_pointer_deleter< another_ptr<X> > deleter; + + if(deleter const * pd = get_deleter<deleter>(px)) + { + another_ptr<X> qx = pd->get(); + } + else + { + // not one of ours + } +} ++
shared_ptr
from a raw pointerSometimes it is necessary to obtain a shared_ptr
given a raw
+ pointer to an object that is already managed by another shared_ptr
+ instance. Example:
void f(X * p) +{ + shared_ptr<X> px(???); +} ++
Inside f
, we'd like to create a shared_ptr
to *p
.
In the general case, this problem has no solution. One approach is to modify f
+ to take a shared_ptr
, if possible:
void f(shared_ptr<X> px); ++
The same transformation can be used for nonvirtual member functions, to convert
+ the implicit this
:
void X::f(int m); ++
would become a free function with a shared_ptr
first argument:
void f(shared_ptr<X> this_, int m); ++
If f
cannot be changed, but X
uses intrusive counting,
+ use make_shared_from_intrusive
described
+ above. Or, if it's known that the shared_ptr
created in f
+ will never outlive the object, use a null deleter.
shared_ptr
(weak_ptr
)
+ to this
in a constructorSome designs require objects to register themselves on construction with a + central authority. When the registration routines take a shared_ptr, this leads + to the question how could a constructor obtain a shared_ptr to this:
+class X +{ +public: + + X() + { + shared_ptr<X> this_(???); + } +}; ++
In the general case, the problem cannot be solved. The X
instance
+ being constructed can be an automatic variable or a static variable; it can be
+ created on the heap:
shared_ptr<X> px(new X);+
but at construction time, px
does not exist yet, and it is
+ impossible to create another shared_ptr
instance that shares
+ ownership with it.
Depending on context, if the inner shared_ptr
this_
doesn't
+ need to keep the object alive, use a null_deleter
as explained
+ here and here. If X
is
+ supposed to always live on the heap, and be managed by a shared_ptr
,
+ use a static factory function:
class X +{ +private: + + X() { ... } + +public: + + static shared_ptr<X> create() + { + shared_ptr<X> px(new X); + // use px as 'this_' + return px; + } +}; ++
shared_ptr
to this
Sometimes it is needed to obtain a shared_ptr
from this
+ in a virtual member function under the assumption that this
is
+ already managed by a shared_ptr
. The transformations
+ described in the previous technique cannot be applied.
A typical example:
+class X +{ +public: + + virtual void f() = 0; + +protected: + + ~X() {} +}; + +class Y +{ +public: + + virtual shared_ptr<X> getX() = 0; + +protected: + + ~Y() {} +}; + +// -- + +class impl: public X, public Y +{ +public: + + impl() { ... } + + virtual void f() { ... } + + virtual shared_ptr<X> getX() + { + shared_ptr<X> px(???); + return px; + } +}; ++
The solution is to keep a weak pointer to this
as a member in impl
:
class impl: public X, public Y +{ +private: + + weak_ptr<impl> weak_this; + + impl(impl const &); + impl & operator=(impl const &); + + impl() { ... } + +public: + + static shared_ptr<impl> create() + { + shared_ptr<impl> pi(new impl); + pi->weak_this = pi; + return pi; + } + + virtual void f() { ... } + + virtual shared_ptr<X> getX() + { + shared_ptr<X> px(weak_this); + return px; + } +}; ++
The library now includes a helper class template
+ enable_shared_from_this
that can be used to encapsulate the
+ solution:
class impl: public X, public Y, public enable_shared_from_this<impl> +{ +public: + + impl(impl const &); + impl & operator=(impl const &); + +public: + + virtual void f() { ... } + + virtual shared_ptr<X> getX() + { + return shared_from_this(); + } +} ++
shared_ptr
as a smart counted handleSome library interfaces use opaque handles, a variation of the + incomplete class technique described above. An example:
+typedef void * HANDLE; +HANDLE CreateProcess(); +void CloseHandle(HANDLE); ++
Instead of a raw pointer, it is possible to use shared_ptr
as the
+ handle and get reference counting and automatic resource management for free:
typedef shared_ptr<void> handle; + +handle createProcess() +{ + shared_ptr<void> pv(CreateProcess(), CloseHandle); + return pv; +} ++
shared_ptr
to execute code on block exitshared_ptr<void>
can automatically execute cleanup code when
+ control leaves a scope.
f(p)
, where p
is a pointer:shared_ptr<void> guard(p, f); ++
f(x, y)
:shared_ptr<void> guard(static_cast<void*>(0), bind(f, x, y)); ++
For a more thorough treatment, see the article "Simplify Your Exception-Safe + Code" by Andrei Alexandrescu and Petru Marginean, available online at + http://www.cuj.com/experts/1812/alexandr.htm?topic=experts.
+shared_ptr<void>
to hold an arbitrary
+ objectshared_ptr<void>
can act as a generic object pointer similar
+ to void*
. When a shared_ptr<void>
instance
+ constructed as:
shared_ptr<void> pv(new X); ++
is destroyed, it will correctly dispose of the X
object by
+ executing ~X
.
This propery can be used in much the same manner as a raw void*
is
+ used to temporarily strip type information from an object pointer. A shared_ptr<void>
+ can later be cast back to the correct type by using
+ static_pointer_cast
.
shared_ptr
+ instancesshared_ptr
and weak_ptr
support operator<
+ comparisons required by standard associative containers such as std::map
.
+ This can be used to non-intrusively associate arbitrary data with objects
+ managed by shared_ptr
:
typedef int Data; + +std::map< shared_ptr<void>, Data > userData; +// or std::map< weak_ptr<void>, Data > userData; to not affect the lifetime + +shared_ptr<X> px(new X); +shared_ptr<int> pi(new int(3)); + +userData[px] = 42; +userData[pi] = 91; ++
shared_ptr
as a CopyConstructible mutex lockSometimes it's necessary to return a mutex lock from a function, and a
+ noncopyable lock cannot be returned by value. It is possible to use shared_ptr
+ as a mutex lock:
class mutex +{ +public: + + void lock(); + void unlock(); +}; + +shared_ptr<mutex> lock(mutex & m) +{ + m.lock(); + return shared_ptr<mutex>(&m, mem_fn(&mutex::unlock)); +} ++
Better yet, the shared_ptr
instance acting as a lock can be
+ encapsulated in a dedicated shared_lock
class:
class shared_lock +{ +private: + + shared_ptr<void> pv; + +public: + + template<class Mutex> explicit shared_lock(Mutex & m): pv((m.lock(), &m), mem_fn(&Mutex::unlock)) {} +}; ++
shared_lock
can now be used as:
shared_lock lock(m); ++
Note that shared_lock
is not templated on the mutex type, thanks to
+ shared_ptr<void>
's ability to hide type information.
shared_ptr
to wrap member function callsshared_ptr
implements the ownership semantics required from the Wrap
/CallProxy
+ scheme described in Bjarne Stroustrup's article "Wrapping C++ Member Function
+ Calls" (available online at http://www.research.att.com/~bs/wrapper.pdf).
+ An implementation is given below:
template<class T> class pointer +{ +private: + + T * p_; + +public: + + explicit pointer(T * p): p_(p) + { + } + + shared_ptr<T> operator->() const + { + p_->prefix(); + return shared_ptr<T>(p_, mem_fn(&T::suffix)); + } +}; + +class X +{ +private: + + void prefix(); + void suffix(); + friend class pointer<X>; + +public: + + void f(); + void g(); +}; + +int main() +{ + X x; + + pointer<X> px(&x); + + px->f(); + px->g(); +} ++
In some situations, a single px.reset()
can trigger an expensive
+ deallocation in a performance-critical region:
class X; // ~X is expensive + +class Y +{ + shared_ptr<X> px; + +public: + + void f() + { + px.reset(); + } +}; ++
The solution is to postpone the potential deallocation by moving px
+ to a dedicated free list that can be periodically emptied when performance and
+ response times are not an issue:
vector< shared_ptr<void> > free_list; + +class Y +{ + shared_ptr<X> px; + +public: + + void f() + { + free_list.push_back(px); + px.reset(); + } +}; + +// periodically invoke free_list.clear() when convenient ++
Another variation is to move the free list logic to the construction point by + using a delayed deleter:
+struct delayed_deleter +{ + template<class T> void operator()(T * p) + { + try + { + shared_ptr<void> pv(p); + free_list.push_back(pv); + } + catch(...) + { + } + } +}; ++
shared_ptr
Make the object hold a shared_ptr
to itself, using a null_deleter
:
class X +{ +private: + + shared_ptr<X> this_; + int i_; + +public: + + explicit X(int i): this_(this, null_deleter()), i_(i) + { + } + + // repeat in all constructors (including the copy constructor!) + + X(X const & rhs): this_(this, null_deleter()), i_(rhs.i_) + { + } + + // do not forget to not assign this_ in the copy assignment + + X & operator=(X const & rhs) + { + i_ = rhs.i_; + } + + weak_ptr<X> get_weak_ptr() const { return this_; } +}; ++
When the object's lifetime ends, X::this_
will be destroyed, and
+ all weak pointers will automatically expire.
$Date$
+Copyright © 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/src/sp_collector.cpp b/src/sp_collector.cpp new file mode 100644 index 0000000..42fd16c --- /dev/null +++ b/src/sp_collector.cpp @@ -0,0 +1,269 @@ +// +// sp_collector.cpp +// +// Copyright (c) 2002, 2003 Peter Dimov +// +// Permission to copy, use, modify, sell and distribute this software +// is granted provided this copyright notice appears in all copies. +// This software is provided "as is" without express or implied +// warranty, and with no claim as to its suitability for any purpose. +// + +#if defined(BOOST_SP_ENABLE_DEBUG_HOOKS) + +#include