diff --git a/gccspeed.gif b/gccspeed.gif new file mode 100644 index 0000000..d78c06b Binary files /dev/null and b/gccspeed.gif differ diff --git a/include/boost/smart_ptr.hpp b/include/boost/smart_ptr.hpp new file mode 100644 index 0000000..d9c0aa5 --- /dev/null +++ b/include/boost/smart_ptr.hpp @@ -0,0 +1,402 @@ +// Boost smart_ptr.hpp header file -----------------------------------------// + +// (C) Copyright Greg Colvin and Beman Dawes 1998, 1999. 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. + +// See http://www.boost.org for most recent version including documentation. + +// Revision History +// 6 Jul 01 Reorder shared_ptr code so VC++ 6 member templates work, allowing +// polymorphic pointers to now work with that compiler (Gary Powell) +// 21 May 01 Require complete type where incomplete type is unsafe. +// (suggested by Vladimir Prus) +// 21 May 01 operator= fails if operand transitively owned by *this, as in a +// linked list (report by Ken Johnson, fix by Beman Dawes) +// 21 Jan 01 Suppress some useless warnings with MSVC (David Abrahams) +// 19 Oct 00 Make shared_ptr ctor from auto_ptr explicit. (Robert Vugts) +// 24 Jul 00 Change throw() to // never throws. See lib guidelines +// Exception-specification rationale. (Beman Dawes) +// 22 Jun 00 Remove #if continuations to fix GCC 2.95.2 problem (Beman Dawes) +// 1 Feb 00 Additional shared_ptr BOOST_NO_MEMBER_TEMPLATES workarounds +// (Dave Abrahams) +// 31 Dec 99 Condition tightened for no member template friend workaround +// (Dave Abrahams) +// 30 Dec 99 Moved BOOST_NMEMBER_TEMPLATES compatibility code to config.hpp +// (Dave Abrahams) +// 30 Nov 99 added operator ==, operator !=, and std::swap and std::less +// specializations for shared types (Darin Adler) +// 11 Oct 99 replaced op[](int) with op[](std::size_t) (Ed Brey, Valentin +// Bonnard), added shared_ptr workaround for no member template +// friends (Matthew Langston) +// 25 Sep 99 added shared_ptr::swap and shared_array::swap (Luis Coelho). +// 20 Jul 99 changed name to smart_ptr.hpp, #include , +// #include and use boost::noncopyable +// 17 May 99 remove scoped_array and shared_array operator*() as +// unnecessary (Beman Dawes) +// 14 May 99 reorder code so no effects when bad_alloc thrown (Abrahams/Dawes) +// 13 May 99 remove certain throw() specifiers to avoid generated try/catch +// code cost (Beman Dawes) +// 11 May 99 get() added, conversion to T* placed in macro guard (Valentin +// Bonnard, Dave Abrahams, and others argued for elimination +// of the automatic conversion) +// 28 Apr 99 #include fix (Valentin Bonnard) +// 28 Apr 99 rename transfer() to share() for clarity (Dave Abrahams) +// 28 Apr 99 remove unsafe shared_array template conversions(Valentin Bonnard) +// 28 Apr 99 p(r) changed to p(r.px) for clarity (Dave Abrahams) +// 21 Apr 99 reset() self assignment fix (Valentin Bonnard) +// 21 Apr 99 dispose() provided to improve clarity (Valentin Bonnard) +// 27 Apr 99 leak when new throws fixes (Dave Abrahams) +// 21 Oct 98 initial Version (Greg Colvin/Beman Dawes) + +#ifndef BOOST_SMART_PTR_HPP +#define BOOST_SMART_PTR_HPP + +#include // for broken compiler workarounds +#include // for std::size_t +#include // for std::auto_ptr +#include // for std::swap +#include // for boost::noncopyable, checked_delete, checked_array_delete +#include // for std::less +#include // for BOOST_STATIC_ASSERT + +#ifdef BOOST_MSVC // moved here to work around VC++ compiler crash +# pragma warning(push) +# pragma warning(disable:4284) // return type for 'identifier::operator->' is not a UDT or reference to a UDT. Will produce errors if applied using infix notation +#endif + +namespace boost { + +// scoped_ptr --------------------------------------------------------------// + +// scoped_ptr mimics a built-in pointer except that it guarantees deletion +// of the object pointed to, either on destruction of the scoped_ptr or via +// an explicit reset(). scoped_ptr is a simple solution for simple needs; +// see shared_ptr (below) or std::auto_ptr if your needs are more complex. + +template class scoped_ptr : noncopyable { + + T* ptr; + + public: + typedef T element_type; + + explicit scoped_ptr( T* p=0 ) : ptr(p) {} // never throws + ~scoped_ptr() { checked_delete(ptr); } + void reset( T* p=0 ) { if ( ptr != p ) { checked_delete(ptr); ptr = p; } } + T& operator*() const { return *ptr; } // never throws + T* operator->() const { return ptr; } // never throws + T* get() const { return ptr; } // never throws +#ifdef BOOST_SMART_PTR_CONVERSION + // get() is safer! Define BOOST_SMART_PTR_CONVERSION at your own risk! + operator T*() const { return ptr; } // never throws +#endif + }; // scoped_ptr + +// scoped_array ------------------------------------------------------------// + +// scoped_array extends scoped_ptr to arrays. Deletion of the array pointed to +// is guaranteed, either on destruction of the scoped_array or via an explicit +// reset(). See shared_array or std::vector if your needs are more complex. + +template class scoped_array : noncopyable { + + T* ptr; + + public: + typedef T element_type; + + explicit scoped_array( T* p=0 ) : ptr(p) {} // never throws + ~scoped_array() { checked_array_delete(ptr); } + + void reset( T* p=0 ) { if ( ptr != p ) + {checked_array_delete(ptr); ptr=p;} } + + T* get() const { return ptr; } // never throws +#ifdef BOOST_SMART_PTR_CONVERSION + // get() is safer! Define BOOST_SMART_PTR_CONVERSION at your own risk! + operator T*() const { return ptr; } // never throws +#else + T& operator[](std::size_t i) const { return ptr[i]; } // never throws +#endif + }; // scoped_array + +// shared_ptr --------------------------------------------------------------// + +// An enhanced relative of scoped_ptr with reference counted copy semantics. +// The object pointed to is deleted when the last shared_ptr pointing to it +// is destroyed or reset. + +template class shared_ptr { + public: + typedef T element_type; + + explicit shared_ptr(T* p =0) : px(p) { + try { pn = new long(1); } // fix: prevent leak if new throws + catch (...) { checked_delete(p); throw; } + } + + ~shared_ptr() { dispose(); } + +#if !defined( BOOST_NO_MEMBER_TEMPLATES ) || defined (BOOST_MSVC6_MEMBER_TEMPLATES) + template + shared_ptr(const shared_ptr& r) : px(r.px) { // never throws + ++*(pn = r.pn); + } +#ifndef BOOST_NO_AUTO_PTR + template + explicit shared_ptr(std::auto_ptr& r) { + pn = new long(1); // may throw + px = r.release(); // fix: moved here to stop leak if new throws + } +#endif + + template + shared_ptr& operator=(const shared_ptr& r) { + share(r.px,r.pn); + return *this; + } + +#ifndef BOOST_NO_AUTO_PTR + template + shared_ptr& operator=(std::auto_ptr& r) { + // code choice driven by guarantee of "no effect if new throws" + if (*pn == 1) { checked_delete(px); } + else { // allocate new reference counter + long * tmp = new long(1); // may throw + --*pn; // only decrement once danger of new throwing is past + pn = tmp; + } // allocate new reference counter + px = r.release(); // fix: moved here so doesn't leak if new throws + return *this; + } +#endif +#else +#ifndef BOOST_NO_AUTO_PTR + explicit shared_ptr(std::auto_ptr& r) { + pn = new long(1); // may throw + px = r.release(); // fix: moved here to stop leak if new throws + } + + shared_ptr& operator=(std::auto_ptr& r) { + // code choice driven by guarantee of "no effect if new throws" + if (*pn == 1) { checked_delete(px); } + else { // allocate new reference counter + long * tmp = new long(1); // may throw + --*pn; // only decrement once danger of new throwing is past + pn = tmp; + } // allocate new reference counter + px = r.release(); // fix: moved here so doesn't leak if new throws + return *this; + } +#endif +#endif + + // The assignment operator and the copy constructor must come after + // the templated versions for MSVC6 to work. (Gary Powell) + shared_ptr(const shared_ptr& r) : px(r.px) { ++*(pn = r.pn); } // never throws + + shared_ptr& operator=(const shared_ptr& r) { + share(r.px,r.pn); + return *this; + } + + void reset(T* p=0) { + if ( px == p ) return; // fix: self-assignment safe + if (--*pn == 0) { checked_delete(px); } + else { // allocate new reference counter + try { pn = new long; } // fix: prevent leak if new throws + catch (...) { + ++*pn; // undo effect of --*pn above to meet effects guarantee + checked_delete(p); + throw; + } // catch + } // allocate new reference counter + *pn = 1; + px = p; + } // reset + + T& operator*() const { return *px; } // never throws + T* operator->() const { return px; } // never throws + T* get() const { return px; } // never throws + #ifdef BOOST_SMART_PTR_CONVERSION + // get() is safer! Define BOOST_SMART_PTR_CONVERSION at your own risk! + operator T*() const { return px; } // never throws + #endif + + long use_count() const { return *pn; } // never throws + bool unique() const { return *pn == 1; } // never throws + + void swap(shared_ptr& other) // never throws + { std::swap(px,other.px); std::swap(pn,other.pn); } + +// Tasteless as this may seem, making all members public allows member templates +// to work in the absence of member template friends. (Matthew Langston) +// Don't split this line into two; that causes problems for some GCC 2.95.2 builds +#if ( defined(BOOST_NO_MEMBER_TEMPLATES) && !defined(BOOST_MSVC6_MEMBER_TEMPLATES) ) || !defined( BOOST_NO_MEMBER_TEMPLATE_FRIENDS ) + private: +#endif + + T* px; // contained pointer + long* pn; // ptr to reference counter + +// Don't split this line into two; that causes problems for some GCC 2.95.2 builds +#if !defined( BOOST_NO_MEMBER_TEMPLATES ) && !defined( BOOST_NO_MEMBER_TEMPLATE_FRIENDS ) + template friend class shared_ptr; +#endif + + void dispose() { if (--*pn == 0) { checked_delete(px); delete pn; } } + + void share(T* rpx, long* rpn) { + if (pn != rpn) { // Q: why not px != rpx? A: fails when both == 0 + ++*rpn; // done before dispose() in case rpn transitively + // dependent on *this (bug reported by Ken Johnson) + dispose(); + px = rpx; + pn = rpn; + } + } // share +}; // shared_ptr + +template + inline bool operator==(const shared_ptr& a, const shared_ptr& b) + { return a.get() == b.get(); } + +template + inline bool operator!=(const shared_ptr& a, const shared_ptr& b) + { return a.get() != b.get(); } + +// shared_array ------------------------------------------------------------// + +// shared_array extends shared_ptr to arrays. +// The array pointed to is deleted when the last shared_array pointing to it +// is destroyed or reset. + +template class shared_array { + public: + typedef T element_type; + + explicit shared_array(T* p =0) : px(p) { + try { pn = new long(1); } // fix: prevent leak if new throws + catch (...) { checked_array_delete(p); throw; } + } + + shared_array(const shared_array& r) : px(r.px) // never throws + { ++*(pn = r.pn); } + + ~shared_array() { dispose(); } + + shared_array& operator=(const shared_array& r) { + if (pn != r.pn) { // Q: why not px != r.px? A: fails when both px == 0 + ++*r.pn; // done before dispose() in case r.pn transitively + // dependent on *this (bug reported by Ken Johnson) + dispose(); + px = r.px; + pn = r.pn; + } + return *this; + } // operator= + + void reset(T* p=0) { + if ( px == p ) return; // fix: self-assignment safe + if (--*pn == 0) { checked_array_delete(px); } + else { // allocate new reference counter + try { pn = new long; } // fix: prevent leak if new throws + catch (...) { + ++*pn; // undo effect of --*pn above to meet effects guarantee + checked_array_delete(p); + throw; + } // catch + } // allocate new reference counter + *pn = 1; + px = p; + } // reset + + T* get() const { return px; } // never throws + #ifdef BOOST_SMART_PTR_CONVERSION + // get() is safer! Define BOOST_SMART_PTR_CONVERSION at your own risk! + operator T*() const { return px; } // never throws + #else + T& operator[](std::size_t i) const { return px[i]; } // never throws + #endif + + long use_count() const { return *pn; } // never throws + bool unique() const { return *pn == 1; } // never throws + + void swap(shared_array& other) // never throws + { std::swap(px,other.px); std::swap(pn,other.pn); } + + private: + + T* px; // contained pointer + long* pn; // ptr to reference counter + + void dispose() { if (--*pn == 0) { checked_array_delete(px); delete pn; } } + +}; // shared_array + +template + inline bool operator==(const shared_array& a, const shared_array& b) + { return a.get() == b.get(); } + +template + inline bool operator!=(const shared_array& a, const shared_array& b) + { return a.get() != b.get(); } + +} // namespace boost + +// specializations for things in namespace std -----------------------------// + +#ifndef BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION + +namespace std { + +// Specialize std::swap to use the fast, non-throwing swap that's provided +// as a member function instead of using the default algorithm which creates +// a temporary and uses assignment. + +template + inline void swap(boost::shared_ptr& a, boost::shared_ptr& b) + { a.swap(b); } + +template + inline void swap(boost::shared_array& a, boost::shared_array& b) + { a.swap(b); } + +// Specialize std::less so we can use shared pointers and arrays as keys in +// associative collections. + +// It's still a controversial question whether this is better than supplying +// a full range of comparison operators (<, >, <=, >=). + +template + struct less< boost::shared_ptr > + : binary_function, boost::shared_ptr, bool> + { + bool operator()(const boost::shared_ptr& a, + const boost::shared_ptr& b) const + { return less()(a.get(),b.get()); } + }; + +template + struct less< boost::shared_array > + : binary_function, boost::shared_array, bool> + { + bool operator()(const boost::shared_array& a, + const boost::shared_array& b) const + { return less()(a.get(),b.get()); } + }; + +} // namespace std + +#endif // ifndef BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION + +#ifdef BOOST_MSVC +# pragma warning(pop) +#endif + +#endif // BOOST_SMART_PTR_HPP + + diff --git a/index.htm b/index.htm new file mode 100644 index 0000000..9c8e78a --- /dev/null +++ b/index.htm @@ -0,0 +1,38 @@ + + + + +Boost Smart Pointer Library + + + + + + + + + + + + + + + +
c++boost.gif (8819 bytes)HomeLibrariesPeopleFAQMore
+

Smart pointer library

+

The header smart_ptr.hpp provides four smart pointer classes.  Smart +pointers ease the management of memory dynamically allocated with C++ new +expressions. +

+

Revised 14 Mar 2001 +

+ + + + 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..518b4cb --- /dev/null +++ b/scoped_array.htm @@ -0,0 +1,102 @@ + + + +scoped_array + + + + + + +

c++boost.gif (8819 bytes)Class +scoped_array

+

Class scoped_array 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 scoped_array::reset().

+

Class scoped_array 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.  By +being 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.

+

Class scoped_array cannot correctly hold a pointer to a +single object.  See scoped_ptr +for that usage.

+

A C++ Standard Library vector is a heavier duty alternative to a scoped_array.

+

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

+

Class scoped_array Synopsis

+
#include <boost/smart_ptr.hpp>
+namespace boost {
+
+template<typename T> class scoped_array : noncopyable {
+
+ public:
+   typedef T element_type;
+
+   explicit scoped_array( T* p=0 );  // never throws
+   ~scoped_array();
+
+   void reset( T* p=0 );
+
+   T& operator[](std::size_t i) const;  // never throws
+   T* get() const;  // never throws
+   };
+}
+

Class scoped_array Members

+

scoped_array element_type

+
typedef T element_type;
+

Provides the type of the stored pointer.

+

scoped_array constructors

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

+

scoped_array destructor

+
~scoped_array();
+

Deletes the array pointed to by the stored pointer.  Note that in C++ delete[] +on a pointer with a value of 0 is harmless.

+

Does not throw exceptions.

+

scoped_array reset

+
void reset( T* p=0 )();
+

If p is not equal to the stored pointer, 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.

+

Does not throw exceptions.

+

scoped_array operator[]

+

T& operator[](std::size_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 get()==0, +or if i is less than 0 or is greater or equal to the number of elements +in the array.

+

scoped_array get

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

T is not required be a complete type.  +See Common Requirements.

+

Returns the stored pointer.

+

Class scoped_array example

+

[To be supplied. In the meantime, see smart_ptr_test.cpp.]

+
+

Revised  24 May, 2001 +

+

© Copyright Greg Colvin and Beman Dawes 1999. 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/scoped_ptr.htm b/scoped_ptr.htm new file mode 100644 index 0000000..652d1bb --- /dev/null +++ b/scoped_ptr.htm @@ -0,0 +1,153 @@ + + + +scoped_ptr + + + + + + +

c++boost.gif (8819 bytes)Class +scoped_ptr

+

Class scoped_ptr 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 scoped_ptr::reset().  +See example.

+

Class scoped_ptr 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 so 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.  (Because of the "complete type" +requirement for delete and reset members, they may have one additional function +call overhead in certain idioms.  See Handle/Body +Idiom.)   

+

Class scoped_ptr cannot be used in C++ Standard Library containers.  See shared_ptr +or std::auto_ptr if scoped_ptr does not meet your needs.

+

Class scoped_ptr cannot correctly hold a pointer to a +dynamically allocated array.  See scoped_array +for that usage.

+

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

+

Class scoped_ptr Synopsis

+
#include <boost/smart_ptr.hpp>
+namespace boost {
+
+template<typename T> class scoped_ptr : noncopyable {
+
+ public:
+   typedef T element_type;
+
+   explicit scoped_ptr( T* p=0 );  // never throws
+   ~scoped_ptr();
+
+   void reset( T* p=0 );
+
+   T& operator*() const;  // never throws
+   T* operator->() const;  // never throws
+   T* get() const;  // never throws
+   };
+}
+

Class scoped_ptr Members

+

scoped_ptr element_type

+
typedef T element_type;
+

Provides the type of the stored pointer.

+

scoped_ptr constructors

+
explicit scoped_ptr( T* p=0 );  // never throws
+

T is not required be a complete type.  +See Common Requirements.

+

Constructs a scoped_ptr, storing a copy of p, which must +have been allocated via a C++ new expression or be 0.

+

scoped_ptr destructor

+
~scoped_ptr();
+

Deletes the object pointed to by the stored pointer.  Note that in C++, delete +on a pointer with a value of 0 is harmless.

+

Does not throw exceptions.

+

scoped_ptr reset

+
void reset( T* p=0 );
+

If p is not equal to the stored pointer, 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.

+

Does not throw exceptions.

+

scoped_ptr operator*

+
T& operator*() const;  // never throws
+

Returns a reference to the object pointed to by the stored pointer.

+

scoped_ptr operator-> and get

+
T* operator->() const;  // never throws
+T* get() const;  // never throws
+

T is not required by get() be a complete type.  See Common Requirements.

+

Both return the stored pointer.

+

Class scoped_ptr examples

+
#include <iostream>
+#include <boost/smart_ptr.h>
+
+struct Shoe { ~Shoe(){ std::cout << "Buckle my shoe" << std::endl; } };
+
+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() << std::endl;
+    std::cout << my_instance.add_one() << std::endl;
+    }
+

The example program produces the beginning of a child's nursery rhyme as +output:

+
+
1
+2
+Buckle my shoe
+
+

Rationale

+

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 actually 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 - you are signaling intent.

+

It has been suggested that boost::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.

+

Handle/Body Idiom

+

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.

+

FAQ

+

Q. Why doesn't scoped_ptr have a release() member?
+A. Because the whole point of scoped_ptr is to signal intent not +to transfer ownership.  Use std::auto_ptr if ownership transfer is +required.

+
+

Revised 24 May 2001

+

© Copyright Greg Colvin and Beman Dawes 1999. 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/scoped_ptr_example.cpp b/scoped_ptr_example.cpp new file mode 100644 index 0000000..3e2e511 --- /dev/null +++ b/scoped_ptr_example.cpp @@ -0,0 +1,16 @@ +// Boost scoped_ptr_example implementation file -----------------------------// + +#include "scoped_ptr_example.hpp" +#include + +class example::implementation +{ + public: + ~implementation() { std::cout << "destroying implementation\n"; } +}; + +example::example() : _imp( new implementation ) {} + +void example::do_something() { std::cout << "did something\n"; } + +example::~example() {} diff --git a/scoped_ptr_example.hpp b/scoped_ptr_example.hpp new file mode 100644 index 0000000..97c8bff --- /dev/null +++ b/scoped_ptr_example.hpp @@ -0,0 +1,21 @@ +// Boost scoped_ptr_example header file ------------------------------------// + +#include + +// The point of this example is to prove that even though +// example::implementation is an incomplete type in translation units using +// this header, scoped_ptr< implementation > is still valid because the type +// is complete where it counts - in the inplementation translation unit where +// destruction is actually instantiated. + +class example : boost::noncopyable +{ + public: + example(); + ~example(); + void do_something(); + private: + class implementation; + boost::scoped_ptr< implementation > _imp; // hide implementation details +}; + diff --git a/scoped_ptr_example_test.cpp b/scoped_ptr_example_test.cpp new file mode 100644 index 0000000..3629ec8 --- /dev/null +++ b/scoped_ptr_example_test.cpp @@ -0,0 +1,10 @@ +// Boost scoped_ptr_example_test main program -------------------------------// + +#include "scoped_ptr_example.hpp" + +int main() +{ + example my_example; + my_example.do_something(); + return 0; +} \ No newline at end of file diff --git a/shared_array.htm b/shared_array.htm new file mode 100644 index 0000000..6de8704 --- /dev/null +++ b/shared_array.htm @@ -0,0 +1,189 @@ + + + +shared_array + + + + + + +

c++boost.gif (8819 bytes)Class +shared_array

+

Class shared_array 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 shared_array, on shared_array::operator=(), +or via an explicit shared_array::reset().  See example.

+

Class shared_array meets the CopyConstuctible +and Assignable requirements of the C++ Standard Library, and so +can be used in C++ Standard Library containers.  A specialization of std:: +less< > for  boost::shared_ptr<Y> is supplied so that  +shared_array works by default for Standard Library's Associative +Container Compare template parameter.  For compilers not supporting partial +specialization, the user must explicitly pass the less<> functor.

+

Class shared_array cannot correctly hold a pointer to a +single object.  See shared_ptr +for that usage.

+

Class shared_array will not work correctly with cyclic data +structures. For example, if main() holds a shared_array pointing to array A, +which directly or indirectly holds a shared_array pointing back to array A, then +array A's use_count() will be 2, and destruction of the main() shared_array will +leave array A dangling with a use_count() of 1.

+

A C++ Standard Library vector is a +heavier duty alternative to a shared_array.

+

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

+

Class shared_array Synopsis

+
#include <boost/smart_ptr.hpp>
+namespace boost {
+
+template<typename T> class shared_array {
+
+ public:
+   typedef T element_type;
+
+   explicit shared_array( T* p=0 );
+   shared_array( const shared_array& );  // never throws   
+   ~shared_array();
+
+   shared_array& operator=( const shared_array& );  // never throws  
+
+   void reset( T* p=0 );
+
+   T& operator[](std::size_t i) const;  // never throws
+   T* get() const;  // never throws
+
+   long use_count() const;  // never throws
+   bool unique() const;  // never throws
+
+   void swap( shared_array<T>& other ) throw()
+   };
+
+template<typename T>
+  inline bool operator==(const shared_array<T>& a, const shared_array<T>& b)
+    { return a.get() == b.get(); }
+
+template<typename T>
+  inline bool operator!=(const shared_array<T>& a, const shared_array<T>& b)
+    { return a.get() != b.get(); }
+}
+
namespace std {
+
+template<typename T>
+  inline void swap(boost::shared_array<T>& a, boost::shared_array<T>& b)
+    { a.swap(b); }
+
+template<typename T>
+  struct less< boost::shared_array<T> >
+    : binary_function<boost::shared_array<T>, boost::shared_array<T>, bool>
+  {
+    bool operator()(const boost::shared_array<T>& a,
+        const boost::shared_array<T>& b) const
+      { return less<T*>()(a.get(),b.get()); }
+  };
+
+} // namespace std 
+

Specialization of std::swap uses the fast, non-throwing swap that's provided +as a member function instead of using the default algorithm which creates a +temporary and uses assignment.
+
+Specialization of std::less allows use of shared arrays as keys in C++ +Standard Library associative collections.
+
+The std::less specializations use std::less<T*> to perform the +comparison.  This insures that pointers are 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).
+
+It's still a controversial question whether supplying only std::less is better +than supplying a full range of comparison operators (<, >, <=, >=).

+

The current implementation does not supply the specializations if the macro +name BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION is defined.

+

Class shared_array Members

+

shared_array element_type

+
typedef T element_type;
+

Provides the type of the stored pointer.

+

shared_array constructors

+
explicit shared_array( T* p=0 );
+

Constructs a shared_array, storing a copy of p, +which must have been allocated via a C++ new[] expression or be 0. +Afterwards, use_count() is 1 (even if p==0; see ~shared_array).

+

The only exception which may be thrown is std::bad_alloc.  If +an exception is thrown,  delete[] p is called.

+
shared_array( const shared_array& r);  // never throws
+

Constructs a shared_array, as if by storing a copy of the +pointer stored in r. Afterwards, use_count() +for all copies is 1 more than the initial r.use_count().

+

shared_array destructor

+
~shared_array();
+

If use_count() == 1, deletes the array pointed to by the +stored pointer. Otherwise, use_count() for any remaining +copies is decremented by 1. Note that in C++ delete[] on a pointer with +a value of 0 is harmless.

+

Does not throw exceptions.

+

shared_array operator=

+
shared_array& operator=( const shared_array& r);  // never throws
+

First, if use_count() == 1, deletes the array pointed to by +the stored pointer. Otherwise, use_count() for any +remaining copies is decremented by 1. Note that in C++ delete[] on a +pointer with a value of 0 is harmless.

+

Then replaces the contents of this, as if by storing a copy +of the pointer stored in r. Afterwards, use_count() +for all copies is 1 more than the initial r.use_count()

+

shared_array reset

+
void reset( T* p=0 );
+

First, if use_count() == 1, deletes the array pointed to by +the stored pointer. Otherwise, use_count() for any +remaining copies is decremented by 1. Note that in C++  delete[] +on a pointer with a value of 0 is harmless.

+

Then replaces the contents of this, as if by storing a copy +of p, which must have been allocated via a C++ new[] +expression or be 0. Afterwards, use_count() is 1 (even if p==0; +see ~shared_array).

+

The only exception which may be thrown is std::bad_alloc.  If +an exception is thrown,  delete[] p is called.

+

shared_array operator[]

+

T& operator[](std::size_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 get()==0, +or if i is less than 0 or is greater or equal to the number of elements +in the array.

+

shared_array get

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

T is not required be a complete type.  +See Common Requirements.

+

Returns the stored pointer.

+

shared_array use_count

+

long use_count() const; // never throws

+

T is not required be a complete type.  +See Common Requirements.

+

Returns the number of shared_arrays sharing ownership of the +stored pointer.

+

shared_array unique

+

bool unique() const; // never throws

+

T is not required be a complete type.  +See Common Requirements.

+

Returns use_count() == 1.

+

shared_array swap

+

void swap( shared_array<T>& other ) throw()

+

T is not required be a complete type.  +See Common Requirements.

+

Swaps the two smart pointers, as if by std::swap.

+

Class shared_array example

+

[To be supplied. In the meantime, see smart_ptr_test.cpp.]

+
+

Revised 24 May, 2001 +

+

© Copyright Greg Colvin and Beman Dawes 1999. 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..c9fe1e6 --- /dev/null +++ b/shared_ptr.htm @@ -0,0 +1,269 @@ + + + +shared_ptr + + + + + + +

c++boost.gif (8819 bytes)Class +shared_ptr

+

Introduction
+Synopsis

+Members
+Example
+Handle/Body Idiom
+Frequently Asked Questions
+Smart Pointer Timings

+

Introduction

+

Class shared_ptr 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 when +the last shared_ptr pointing to it is deleted or reset.  +See example.

+

Class shared_ptr meets the CopyConstuctible +and Assignable requirements of the C++ Standard Library, and so +can be used in C++ Standard Library containers.  A specialization of std:: +less< > for  boost::shared_ptr<Y> is supplied so that  +shared_ptr works by default for Standard Library's Associative +Container Compare template parameter.  For compilers not supporting partial +specialization, the user must explicitly pass the less<> functor.

+

Class shared_ptr cannot correctly hold a pointer to a +dynamically allocated array.  See shared_array +for that usage.

+

Class shared_ptr will not work correctly with cyclic data +structures. For example, if main() holds a shared_ptr to object A, which +directly or indirectly holds a shared_ptr back to object A, then object A's +use_count() will be 2, and destruction of the main() shared_ptr will leave +object A dangling with a use_count() of 1.

+

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

+

Class shared_ptr Synopsis

+
#include <boost/smart_ptr.hpp>
+namespace boost {
+
+template<typename T> class shared_ptr {
+
+ public:
+   typedef T element_type;
+
+   explicit shared_ptr( T* p=0 );
+   ~shared_ptr();
+
+   shared_ptr( const shared_ptr& );   
+   template<typename Y>
+      shared_ptr(const shared_ptr<Y>& r);  // never throws
+   template<typename Y>
+      shared_ptr(std::auto_ptr<Y>& r);
+
+   shared_ptr& operator=( const shared_ptr& );  // never throws  
+   template<typename Y>
+      shared_ptr& operator=(const shared_ptr<Y>& r);  // never throws
+   template<typename Y>
+      shared_ptr& operator=(std::auto_ptr<Y>& r);
+
+   void reset( T* p=0 );
+
+   T& operator*() const;  // never throws
+   T* operator->() const;  // never throws
+   T* get() const;  // never throws
+
+   long use_count() const;  // never throws
+   bool unique() const;  // never throws
+
+   void swap( shared_ptr<T>& other ) throw()
+   };
+
+template<typename T, typename U>
+  inline bool operator==(const shared_ptr<T>& a, const shared_ptr<U>& b)
+    { return a.get() == b.get(); }
+
+template<typename T, typename U>
+  inline bool operator!=(const shared_ptr<T>& a, const shared_ptr<U>& b)
+    { return a.get() != b.get(); }
+}
+
namespace std {
+
+template<typename T>
+  inline void swap(boost::shared_ptr<T>& a, boost::shared_ptr<T>& b)
+    { a.swap(b); }
+
+template<typename T>
+  struct less< boost::shared_ptr<T> >
+    : binary_function<boost::shared_ptr<T>, boost::shared_ptr<T>, bool>
+  {
+    bool operator()(const boost::shared_ptr<T>& a,
+        const boost::shared_ptr<T>& b) const
+      { return less<T*>()(a.get(),b.get()); }
+  };
+
+} // namespace std 
+

Specialization of std::swap uses the fast, non-throwing swap that's provided +as a member function instead of using the default algorithm which creates a +temporary and uses assignment.
+
+Specialization of std::less allows use of shared pointers as keys in C++ +Standard Library associative collections.
+
+The std::less specializations use std::less<T*> to perform the +comparison.  This insures that pointers are 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).
+
+It's still a controversial question whether supplying only std::less is better +than supplying a full range of comparison operators (<, >, <=, >=).

+

The current implementation does not supply the specializations if the macro +name BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION is defined.

+

The current implementation does not supply the member template functions if +the macro name BOOST_NO_MEMBER_TEMPLATES is defined.

+

Class shared_ptr Members

+

shared_ptr element_type

+
typedef T element_type;
+

Provides the type of the stored pointer.

+

shared_ptr constructors

+
explicit shared_ptr( T* p=0 );
+

Constructs a shared_ptr, storing a copy of p, which +must have been allocated via a C++ new expression or be 0. Afterwards, use_count() +is 1 (even if p==0; see ~shared_ptr).

+

The only exception which may be thrown by this constructor is std::bad_alloc.   +If an exception is thrown,  delete p is called.

+
shared_ptr( const shared_ptr& r);  // never throws   
+template<typename Y>
+   shared_ptr(const shared_ptr<Y>& r);  // never throws
+template<typename Y>
+   shared_ptr(std::auto_ptr<Y>& r);
+

Constructs a shared_ptr, as if by storing a copy of the +pointer stored in r. Afterwards, use_count() +for all copies is 1 more than the initial r.use_count(), or 1 +in the auto_ptr case. In the auto_ptr case, r.release() +is called.

+

The only exception which may be thrown by the constructor from auto_ptr +is std::bad_alloc.   If an exception is thrown, that +constructor has no effect.

+

shared_ptr destructor

+
~shared_ptr();
+

If use_count() == 1, deletes the object pointed to by the +stored pointer. Otherwise, use_count() for any remaining +copies is decremented by 1. Note that in C++  delete on a pointer +with a value of 0 is harmless.

+

Does not throw exceptions.

+

shared_ptr operator=

+
shared_ptr& operator=( const shared_ptr& r);  
+template<typename Y>
+   shared_ptr& operator=(const shared_ptr<Y>& r);
+template<typename Y>
+   shared_ptr& operator=(std::auto_ptr<Y>& r);
+

First, if use_count() == 1, deletes the object pointed to by +the stored pointer. Otherwise, use_count() for any +remaining copies is decremented by 1. Note that in C++  delete on +a pointer with a value of 0 is harmless.

+

Then replaces the contents of this, as if by storing a copy +of the pointer stored in r. Afterwards, use_count() +for all copies is 1 more than the initial r.use_count(), or 1 +in the auto_ptr case. In the auto_ptr case, r.release() +is called.

+

The first two forms of operator= above do not throw exceptions.

+

The only exception which may be thrown by the auto_ptr form +is std::bad_alloc.   If an exception is thrown, the function +has no effect.

+

shared_ptr reset

+
void reset( T* p=0 );
+

First, if use_count() == 1, deletes the object pointed to by +the stored pointer. Otherwise, use_count() for any +remaining copies is decremented by 1. 

+

Then replaces the contents of this, as if by storing a copy +of p, which must have been allocated via a C++ new +expression or be 0. Afterwards, use_count() is 1 (even if p==0; +see ~shared_ptr). Note that in C++  delete +on a pointer with a value of 0 is harmless.

+

The only exception which may be thrown is std::bad_alloc.  If +an exception is thrown,  delete p is called.

+

shared_ptr operator*

+
T& operator*() const;  // never throws
+

Returns a reference to the object pointed to by the stored pointer.

+

shared_ptr operator-> and get

+
T* operator->() const;  // never throws
+T* get() const;  // never throws
+

T is not required by get() to be a complete type .  See Common Requirements.

+

Both return the stored pointer.

+

shared_ptr use_count

+

long use_count() const; // never throws

+

T is not required be a complete type.  +See Common Requirements.

+

Returns the number of shared_ptrs sharing ownership of the +stored pointer.

+

shared_ptr unique

+

bool unique() const; // never throws

+

T is not required be a complete type.  +See Common Requirements.

+

Returns use_count() == 1.

+

shared_ptr swap

+

void swap( shared_ptr<T>& other ) throw()

+

T is not required be a complete type.  +See Common Requirements.

+

Swaps the two smart pointers, as if by std::swap.

+

Class shared_ptr example

+

See shared_ptr_example.cpp for a complete example program.

+

This program builds a std::vector and std::set of FooPtr's.

+

Note that after the two containers have been populated, some of the FooPtr objects +will have use_count()==1 rather than use_count()==2, since foo_set is a std::set +rather than a std::multiset, and thus does not contain duplicate entries.  Furthermore, 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.  Without using a smart pointer, memory and +exception management would be a nightmare.

+

Handle/Body Idiom

+

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.

+

Frequently Asked Questions

+

Q. Why doesn't shared_ptr have template parameters supplying +traits or policies to allow extensive user customization?
+A. Parameterization discourages users.  Shared_ptr is +carefully crafted to meet common needs without extensive parameterization. +Someday 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. 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.

+

Q. Why don't shared_ptr (and 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 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 (your pet feature here)?
+A. Because (your pet feature here) would mandate a reference counted (or a link-list, or ...) implementation. This is not the intent. +[Provided by Peter Dimov]
+

+
+

Revised 13 July, 2001 +

+

© Copyright Greg Colvin and Beman Dawes 1999. 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_example.cpp b/shared_ptr_example.cpp new file mode 100644 index 0000000..988efbd --- /dev/null +++ b/shared_ptr_example.cpp @@ -0,0 +1,96 @@ +// Boost shared_ptr_example.cpp --------------------------------------------// + +// (C) Copyright Beman Dawes 2001. 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. + +// See http://www.boost.org for most recent version including documentation. + +// Revision History +// 21 May 01 Initial complete version (Beman Dawes) + +// The original code for this example appeared in the shared_ptr documentation. +// Ray Gallimore pointed out that foo_set was missing a Compare template +// argument, so would not work as intended. At that point the code was +// turned into an actual .cpp file so it could be compiled and tested. + +#include +#include +#include +#include +#include + +// The application will produce a series of +// objects of type Foo which later must be +// accessed both by occurrence (std::vector) +// and by ordering relationship (std::set). + +struct Foo +{ + Foo( int _x ) : x(_x) {} + ~Foo() { std::cout << "Destructing a Foo with x=" << x << "\n"; } + int x; + /* ... */ +}; + +typedef boost::shared_ptr FooPtr; + +struct FooPtrOps +{ + bool operator()( const FooPtr & a, const FooPtr & b ) + { return a->x > b->x; } + void operator()( const FooPtr & a ) + { std::cout << a->x << "\n"; } +}; + +int main() +{ + std::vector foo_vector; + std::set foo_set; // NOT multiset! + + FooPtr foo_ptr( new Foo( 2 ) ); + foo_vector.push_back( foo_ptr ); + foo_set.insert( foo_ptr ); + + foo_ptr.reset( new Foo( 1 ) ); + foo_vector.push_back( foo_ptr ); + foo_set.insert( foo_ptr ); + + foo_ptr.reset( new Foo( 3 ) ); + foo_vector.push_back( foo_ptr ); + foo_set.insert( foo_ptr ); + + foo_ptr.reset ( new Foo( 2 ) ); + foo_vector.push_back( foo_ptr ); + foo_set.insert( foo_ptr ); + + std::cout << "foo_vector:\n"; + std::for_each( foo_vector.begin(), foo_vector.end(), FooPtrOps() ); + + std::cout << "\nfoo_set:\n"; + std::for_each( foo_set.begin(), foo_set.end(), FooPtrOps() ); + std::cout << "\n"; + +// Expected output: +// +// foo_vector: +// 2 +// 1 +// 3 +// 2 +// +// foo_set: +// 3 +// 2 +// 1 +// +// Destructing a Foo with x=2 +// Destructing a Foo with x=1 +// Destructing a Foo with x=3 +// Destructing a Foo with x=2 + + return 0; +} + diff --git a/shared_ptr_example2.cpp b/shared_ptr_example2.cpp new file mode 100644 index 0000000..eddc806 --- /dev/null +++ b/shared_ptr_example2.cpp @@ -0,0 +1,21 @@ +// Boost shared_ptr_example2 implementation file -----------------------------// + +#include "shared_ptr_example2.hpp" +#include + +class example::implementation +{ + public: + ~implementation() { std::cout << "destroying implementation\n"; } +}; + +example::example() : _imp( new implementation ) {} +example::example( const example & s ) : _imp( s._imp ) {} + +example & example::operator=( const example & s ) + { _imp = s._imp; return *this; } + +void example::do_something() + { std::cout << "use_count() is " << _imp.use_count() << "\n"; } + +example::~example() {} diff --git a/shared_ptr_example2.hpp b/shared_ptr_example2.hpp new file mode 100644 index 0000000..fea810b --- /dev/null +++ b/shared_ptr_example2.hpp @@ -0,0 +1,27 @@ +// Boost shared_ptr_example2 header file -----------------------------------// + +#include + +// This example demonstrates the handle/body idiom (also called pimpl and +// several other names). It separates the interface (in this header file) +// from the implementation (in shared_ptr_example2.cpp). + +// Note that even though example::implementation is an incomplete type in +// some translation units using this header, shared_ptr< implementation > +// is still valid because the type is complete where it counts - in the +// shared_ptr_example2.cpp translation unit where functions requiring a +// complete type are actually instantiated. + +class example +{ + public: + example(); + ~example(); + example( const example & ); + example & operator=( const example & ); + void do_something(); + private: + class implementation; + boost::shared_ptr< implementation > _imp; // hide implementation details +}; + diff --git a/shared_ptr_example2_test.cpp b/shared_ptr_example2_test.cpp new file mode 100644 index 0000000..1e9886b --- /dev/null +++ b/shared_ptr_example2_test.cpp @@ -0,0 +1,15 @@ +// Boost shared_ptr_example2_test main program ------------------------------// + +#include "shared_ptr_example2.hpp" + +int main() +{ + example a; + a.do_something(); + example b(a); + b.do_something(); + example c; + c = a; + c.do_something(); + return 0; +} \ No newline at end of file diff --git a/smart_ptr.htm b/smart_ptr.htm new file mode 100644 index 0000000..07538e4 --- /dev/null +++ b/smart_ptr.htm @@ -0,0 +1,155 @@ + + + + + + +Smart Pointer Classes + + + + +

c++boost.gif (8819 bytes)Smart +Pointers

+

Smart pointers are classes 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 header boost/smart_ptr.hpp +provides four smart pointer template classes:

+
+ + + + + + + + + + + + + + + + + +
+

scoped_ptr

Simple sole ownership of single objects. Noncopyable.
scoped_arraySimple sole ownership of arrays. Noncopyable.
shared_ptrObject ownership shared among multiple pointers
shared_arrayArray ownership shared among multiple pointers.
+
+

These classes are designed to complement the C++ Standard Library auto_ptr +class.

+

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 Smart Pointer Timings will be of +interest to those curious about performance issues.

+

Common requirements

+

These smart pointer classes have a template parameter, T, which +specifies the type of the object pointed to by the smart pointer.  The +behavior of all four classes 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 checked_delete().

+

Rationale

+

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.

+

Exception safety

+

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

+

Exception-specifications are not used; see exception-specification +rationale.

+

All four classes 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.

+

History and acknowledgements

+

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.  See the +revision history in smart_ptr.hpp +for the specific changes made as a result of their constructive criticism.

+

Oct, 1998.  In 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.  +The committee document was 94-168/N0555, Exception Safe Smart Pointers.  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.

+

Beman Dawes proposed reviving the original semantics under the names safe_ptr +and counted_ptr at an October, 1998, 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 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: +

    +
  • Direct detached: the shared_ptr contains a pointer to the object, and a + pointer to the count.
  • +
  • Indirect detached: the shared_ptr contains a pointer to a helper object, + which in turn contains a pointer to the object and the count.
  • +
  • Embedded attached: the count is a member of the object pointed to.
  • +
  • Placement attached: the count is attached via operator new manipulations.
  • +
+

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.

+

See the Revision History section of the header for further contributors.

+
+

Revised  24 May 2001

+

© Copyright Greg Colvin and Beman Dawes 1999. 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_test.cpp b/smart_ptr_test.cpp new file mode 100644 index 0000000..62c97ca --- /dev/null +++ b/smart_ptr_test.cpp @@ -0,0 +1,289 @@ +// smart pointer test program ----------------------------------------------// + +// (C) Copyright Beman Dawes 1998, 1999. 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. + +// Revision History +// 24 May 01 use Boost test library for error detection, reporting, add tests +// for operations on incomplete types (Beman Dawes) +// 29 Nov 99 added std::swap and associative container tests (Darin Adler) +// 25 Sep 99 added swap tests +// 20 Jul 99 header name changed to .hpp +// 20 Apr 99 additional error tests added. + +#define BOOST_INCLUDE_MAIN +#include +#include +#include +#include +#include + +class Incomplete; + +Incomplete * get_ptr( boost::shared_ptr& incomplete ) +{ + return incomplete.get(); +} + +using namespace std; +using boost::scoped_ptr; +using boost::scoped_array; +using boost::shared_ptr; +using boost::shared_array; + +template +void ck( const T* v1, T v2 ) { BOOST_TEST( *v1 == v2 ); } + +namespace { + int UDT_use_count; // independent of pointer maintained counts + } + +// user defined type -------------------------------------------------------// + +class UDT { + long value_; + public: + explicit UDT( long value=0 ) : value_(value) { ++UDT_use_count; } + ~UDT() { + --UDT_use_count; + cout << "UDT with value " << value_ << " being destroyed" << endl; + } + long value() const { return value_; } + void value( long v ) { value_ = v;; } + }; // UDT + +// tests on incomplete types -----------------------------------------------// + +// Certain smart pointer operations are specified to work on incomplete types, +// and some uses depend upon this feature. These tests verify compilation +// only - the functions aren't actually invoked. + +class Incomplete; + +Incomplete * check_incomplete( scoped_ptr& incomplete ) +{ + return incomplete.get(); +} + +Incomplete * check_incomplete( shared_ptr& incomplete, + shared_ptr& i2 ) +{ + incomplete.swap(i2); + cout << incomplete.use_count() << " " << incomplete.unique() << endl; + return incomplete.get(); +} +// main --------------------------------------------------------------------// + +// This isn't a very systematic test; it just hits some of the basics. + +int test_main( int, char ** ) { + + BOOST_TEST( UDT_use_count == 0 ); // reality check + + // test scoped_ptr with a built-in type + long * lp = new long; + scoped_ptr sp ( lp ); + BOOST_TEST( sp.get() == lp ); + BOOST_TEST( lp == sp.get() ); + BOOST_TEST( &*sp == lp ); + + *sp = 1234568901L; + BOOST_TEST( *sp == 1234568901L ); + BOOST_TEST( *lp == 1234568901L ); + ck( static_cast(sp.get()), 1234568901L ); + ck( lp, *sp ); + + sp.reset(); + BOOST_TEST( sp.get() == 0 ); + + // test scoped_ptr with a user defined type + scoped_ptr udt_sp ( new UDT( 999888777 ) ); + BOOST_TEST( udt_sp->value() == 999888777 ); + udt_sp.reset(); + udt_sp.reset( new UDT( 111222333 ) ); + BOOST_TEST( udt_sp->value() == 111222333 ); + udt_sp.reset( new UDT( 333222111 ) ); + BOOST_TEST( udt_sp->value() == 333222111 ); + + // test scoped_array with a build-in type + char * sap = new char [ 100 ]; + scoped_array sa ( sap ); + BOOST_TEST( sa.get() == sap ); + BOOST_TEST( sap == sa.get() ); + + strcpy( sa.get(), "Hot Dog with mustard and relish" ); + BOOST_TEST( strcmp( sa.get(), "Hot Dog with mustard and relish" ) == 0 ); + BOOST_TEST( strcmp( sap, "Hot Dog with mustard and relish" ) == 0 ); + + BOOST_TEST( sa[0] == 'H' ); + BOOST_TEST( sa[30] == 'h' ); + + sa[0] = 'N'; + sa[4] = 'd'; + BOOST_TEST( strcmp( sap, "Not dog with mustard and relish" ) == 0 ); + + sa.reset(); + BOOST_TEST( sa.get() == 0 ); + + // test shared_ptr with a built-in type + int * ip = new int; + shared_ptr cp ( ip ); + BOOST_TEST( ip == cp.get() ); + BOOST_TEST( cp.use_count() == 1 ); + + *cp = 54321; + BOOST_TEST( *cp == 54321 ); + BOOST_TEST( *ip == 54321 ); + ck( static_cast(cp.get()), 54321 ); + ck( static_cast(ip), *cp ); + + shared_ptr cp2 ( cp ); + BOOST_TEST( ip == cp2.get() ); + BOOST_TEST( cp.use_count() == 2 ); + BOOST_TEST( cp2.use_count() == 2 ); + + BOOST_TEST( *cp == 54321 ); + BOOST_TEST( *cp2 == 54321 ); + ck( static_cast(cp2.get()), 54321 ); + ck( static_cast(ip), *cp2 ); + + shared_ptr cp3 ( cp ); + BOOST_TEST( cp.use_count() == 3 ); + BOOST_TEST( cp2.use_count() == 3 ); + BOOST_TEST( cp3.use_count() == 3 ); + cp.reset(); + BOOST_TEST( cp2.use_count() == 2 ); + BOOST_TEST( cp3.use_count() == 2 ); + BOOST_TEST( cp.use_count() == 1 ); + cp.reset( new int ); + *cp = 98765; + BOOST_TEST( *cp == 98765 ); + *cp3 = 87654; + BOOST_TEST( *cp3 == 87654 ); + BOOST_TEST( *cp2 == 87654 ); + cp.swap( cp3 ); + BOOST_TEST( *cp == 87654 ); + BOOST_TEST( *cp2 == 87654 ); + BOOST_TEST( *cp3 == 98765 ); + cp.swap( cp3 ); + BOOST_TEST( *cp == 98765 ); + BOOST_TEST( *cp2 == 87654 ); + BOOST_TEST( *cp3 == 87654 ); + cp2 = cp2; + BOOST_TEST( cp2.use_count() == 2 ); + BOOST_TEST( *cp2 == 87654 ); + cp = cp2; + BOOST_TEST( cp2.use_count() == 3 ); + BOOST_TEST( *cp2 == 87654 ); + BOOST_TEST( cp.use_count() == 3 ); + BOOST_TEST( *cp == 87654 ); + + shared_ptr cp4; + swap( cp2, cp4 ); + BOOST_TEST( cp4.use_count() == 3 ); + BOOST_TEST( *cp4 == 87654 ); + BOOST_TEST( cp2.get() == 0 ); + +#ifndef BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION + set< shared_ptr > scp; + scp.insert(cp4); + BOOST_TEST( scp.find(cp4) != scp.end() ); + BOOST_TEST( scp.find(cp4) == scp.find( shared_ptr(cp4) ) ); +#endif + + // test shared_array with a built-in type + char * cap = new char [ 100 ]; + shared_array ca ( cap ); + BOOST_TEST( ca.get() == cap ); + BOOST_TEST( cap == ca.get() ); + BOOST_TEST( &ca[0] == cap ); + + strcpy( ca.get(), "Hot Dog with mustard and relish" ); + BOOST_TEST( strcmp( ca.get(), "Hot Dog with mustard and relish" ) == 0 ); + BOOST_TEST( strcmp( cap, "Hot Dog with mustard and relish" ) == 0 ); + + BOOST_TEST( ca[0] == 'H' ); + BOOST_TEST( ca[30] == 'h' ); + + shared_array ca2 ( ca ); + shared_array ca3 ( ca2 ); + + ca[0] = 'N'; + ca[4] = 'd'; + BOOST_TEST( strcmp( ca.get(), "Not dog with mustard and relish" ) == 0 ); + BOOST_TEST( strcmp( ca2.get(), "Not dog with mustard and relish" ) == 0 ); + BOOST_TEST( strcmp( ca3.get(), "Not dog with mustard and relish" ) == 0 ); + BOOST_TEST( ca.use_count() == 3 ); + BOOST_TEST( ca2.use_count() == 3 ); + BOOST_TEST( ca3.use_count() == 3 ); + ca2.reset(); + BOOST_TEST( ca.use_count() == 2 ); + BOOST_TEST( ca3.use_count() == 2 ); + BOOST_TEST( ca2.use_count() == 1 ); + + ca.reset(); + BOOST_TEST( ca.get() == 0 ); + + shared_array ca4; + swap( ca3, ca4 ); + BOOST_TEST( ca4.use_count() == 1 ); + BOOST_TEST( strcmp( ca4.get(), "Not dog with mustard and relish" ) == 0 ); + BOOST_TEST( ca3.get() == 0 ); + +#ifndef BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION + set< shared_array > sca; + sca.insert(ca4); + BOOST_TEST( sca.find(ca4) != sca.end() ); + BOOST_TEST( sca.find(ca4) == sca.find( shared_array(ca4) ) ); +#endif + + // test shared_array with user defined type + shared_array udta ( new UDT[3] ); + + udta[0].value( 111 ); + udta[1].value( 222 ); + udta[2].value( 333 ); + shared_array udta2 ( udta ); + + BOOST_TEST( udta[0].value() == 111 ); + BOOST_TEST( udta[1].value() == 222 ); + BOOST_TEST( udta[2].value() == 333 ); + BOOST_TEST( udta2[0].value() == 111 ); + BOOST_TEST( udta2[1].value() == 222 ); + BOOST_TEST( udta2[2].value() == 333 ); + udta2.reset(); + BOOST_TEST( udta2.get() == 0 ); + BOOST_TEST( udta.use_count() == 1 ); + BOOST_TEST( udta2.use_count() == 1 ); + + BOOST_TEST( UDT_use_count == 4 ); // reality check + + // test shared_ptr with a user defined type + UDT * up = new UDT; + shared_ptr sup ( up ); + BOOST_TEST( up == sup.get() ); + BOOST_TEST( sup.use_count() == 1 ); + + sup->value( 54321 ) ; + BOOST_TEST( sup->value() == 54321 ); + BOOST_TEST( up->value() == 54321 ); + + shared_ptr sup2; + sup2 = sup; + BOOST_TEST( sup2->value() == 54321 ); + BOOST_TEST( sup.use_count() == 2 ); + BOOST_TEST( sup2.use_count() == 2 ); + sup2 = sup2; + BOOST_TEST( sup2->value() == 54321 ); + BOOST_TEST( sup.use_count() == 2 ); + BOOST_TEST( sup2.use_count() == 2 ); + + cout << "OK" << endl; + + new char[12345]; // deliberate memory leak to verify leaks detected + + return 0; + } // main + 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..ad0fbb1 --- /dev/null +++ b/smarttests.htm @@ -0,0 +1,540 @@ + + +boost: smart pointer tests + + + + +

c++boost.gif (8819 bytes)Smart +Pointers Timings

+ +

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.

+

Description

+

Two tests were run: the first aimed to obtain timings for two basic individual + operations:

+
    +
  1. Initial construction from raw pointer.
  2. +
  3. An amortized copy operation consisting of half an assignment and half a + copy construction - designed to reflect average usage.
  4. +
+

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:

+
    +
  1. Counted pointer using a heap allocated reference count, this is referred + to as simple counted.
  2. +
  3. Counted pointer using a special purpose allocator for the reference count + - special counted.
  4. +
  5. Counted pointer using an intrusive reference count - intrusive.
  6. +
  7. Linked pointer as described above - linked.
  8. +
  9. Cyclic pointer, a counted implementation using a std::deque for allocation + with provision for weak pointers and garbage collection of cycles of pointers + - cyclic.
  10. +
+

on two compilers:

+
    +
  1. MSVC 6.0 service pack 3, using default release optimization mode (/O2 - + optimized for speed, no inlining of functions defined outside a class body + unless specified as inline).
  2. +
  3. gcc 2.95.2 using full optimization (-O3 -DNDEBUG).
  4. +
+

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

+

 

+

Operation Timing Test Results

+

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) : -

+

 

+

MSVC

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
initialization
+
copy operation
+
simple counted
+
3000 +/- 170104 +/- 31
+
special counted
+
1330 +/- 5085 +/- 9
+
intrusive
+
1000 +/- 2071 +/- 3
linked970 +/- 60136 +/- 10
cyclic1290 +/- 70112 +/- 12
dumb1020 +/- 2010 +/- 4
+
raw
+
1038 +/- 3010 +/- 5
+

 

+

GCC

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
initialization
+
copy operation
+
simple counted
+
4620 +/- 150301 +/- 28
+
special counted
+
1990 +/- 40264 +/- 7
+
intrusive
+
1590 +/- 70181 +/- 12
linked1470 +/- 140345 +/- 26
cyclic2180 +/- 100330 +/- 18
dumb1590 +/- 7074 +/- 12
+
raw
+
1430 +/- 6027 +/- 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.

+

Detail

+

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.

+

 

+

Container Test Results

+

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.

+

GCC

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 vectorlist
+
+
+
fill
+
sortfillsort
+
simple counted
+
46.542.4447.093.22
+
special counted
+
14.022.837.283.21
+
intrusive
+
12.151.917.993.08
linked12.462.328.143.27
cyclic22.603.191.633.18
+
raw
+
11.810.2427.510.77
+

 

+

MSVC

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 vectorlist
+
+
+
fill
+
sortfillsort
+
simple counted
+
1.832.371.864.85
+
special counted
+
1.042.351.384.58
+
intrusive
+
1.041.841.164.29
linked1.082.001.214.33
cyclic1.382.841.474.73
+
raw
+
0.670.281.241.81
+

 

+

Code Size

+

The following code sizes were determined by inspection of generated code for + MSVC only. Sizes are given in the form N / M / I where:

+
    +
  • N is the instruction count of the operation
  • +
  • M is the size of the code in bytes
  • +
  • I determines whether generated code was inlined or not I = inline, O = "outline"
  • +
+

 

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
ptr()
+
ptr(p)ptr(ptr)op=() +
~ptr()
+
+
simple counted
+
38/110/O38/110/O9/23/I22/57/I17/40/I
+
special counted
+
50/141/O50/141/O9/23/I23/64/I13/38/I
+
intrusive
+
1/2/I3/6/I3/6/I6/11/I6/11/I
+
linked
+
5/19/I5/15/I10/30/I27/59/I14/38/I
+

During the code inspection, a couple of minor points were noticed: -

+
    +
  • Function inlining was critical to performance.
  • +
  • For MSVC, at least, a "delete 0" caused execution of 11 assembly + instructions, including a function call. So in cases where performance is + at an absolute premium it can be worth inserting the extra manual test.
  • +
+

 

+

Data Size

+

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

 

+

Summary

+

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

+
    +
  • Deterministic individual, as opposed to amortized, operation time. This + weighs against any implementation depending on an allocator.
  • +
  • Multithreaded synchronization. This weighs against an implementation which + spreads its information as in the case of linked pointer.
  • +
+
+

Revised 17 Aug 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.

+ +