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..59e42a7 --- /dev/null +++ b/include/boost/smart_ptr.hpp @@ -0,0 +1,379 @@ +// 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 +// 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 +#include // for std::less + +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() { delete ptr; } + + void reset( T* p=0 ) { if ( ptr != p ) { 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() { delete [] ptr; } + + void reset( T* p=0 ) { if ( ptr != p ) {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 (...) { delete p; throw; } + } + + shared_ptr(const shared_ptr& r) : px(r.px) { ++*(pn = r.pn); } // never throws + + ~shared_ptr() { dispose(); } + + shared_ptr& operator=(const shared_ptr& r) { + share(r.px,r.pn); + return *this; + } + +#if !defined( BOOST_NO_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) { 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) { 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 + + void reset(T* p=0) { + if ( px == p ) return; // fix: self-assignment safe + if (--*pn == 0) { 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 + 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_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) { delete px; delete pn; } } + + void share(T* rpx, long* rpn) { + if (pn != rpn) { + 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 (...) { 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) { + 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) { 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 + 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) { 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 + +#endif // BOOST_SMART_PTR_HPP + + diff --git a/index.htm b/index.htm new file mode 100644 index 0000000..da4e16f --- /dev/null +++ b/index.htm @@ -0,0 +1,39 @@ + + + + +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 10 Nov 2000 +

+ + + + 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..3bf65cd --- /dev/null +++ b/scoped_array.htm @@ -0,0 +1,90 @@ + + + +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 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.

+

Because scoped_array is so simple, in its usual +implementation every operation is as fast as a built-in array pointer and has no +more space overhead that a built-in array pointer.

+

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

+

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.

+

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
+

Returns the stored pointer.

+

Class scoped_array example

+

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

+
+

Revised  December 8, 1999

+

© 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..3430481 --- /dev/null +++ b/scoped_ptr.htm @@ -0,0 +1,128 @@ + + + +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 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.

+

Because scoped_ptr is so simple, in its usual implementation +every operation is as fast as a built-in pointer and has no more space overhead +that a built-in pointer.

+

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
+

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
+

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

Handle/Body Idiom

+

One common usage of shared_pointer is to implement a handle/body +structure which avoids exposing the body (implementation) in the header file:

+
class handle
+{
+public:    // simple forwarding functions to the body class
+    void f();
+    void g(int);
+private:
+    friend class body;  //incomplete class hides implementation
+    boost::scoped_ptr<body> imp;
+};
+

This code requires that class body have a trivial destructor to +avoid undefined behavior.  This is because the definition of class +body is not visible at the time scoped_ptr<> deletes it. See ISO +5.3.5/5.  Note that some compilers will issue a warning even though the +above code is well defined.

+
+

Revised 24 July 2000

+

© 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_array.htm b/shared_array.htm new file mode 100644 index 0000000..89d9339 --- /dev/null +++ b/shared_array.htm @@ -0,0 +1,180 @@ + + + +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 heavier duty alternative to a shared_array is a shared_ptr +to a C++ Standard Library vector.

+

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
+

Returns the stored pointer.

+

shared_array use_count

+

long use_count() const; // never throws

+

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

+

shared_array unique

+

bool unique() const; // never throws

+

Returns use_count() == 1.

+

shared_array swap

+

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

+

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 December 8, 1999

+

© 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..3fdca13 --- /dev/null +++ b/shared_ptr.htm @@ -0,0 +1,206 @@ + + + +shared_ptr + + + + + + +

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

+

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
+

Both return the stored pointer.

+

shared_ptr use_count

+

long use_count() const; // never throws

+

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

+

shared_ptr unique

+

bool unique() const; // never throws

+

Returns use_count() == 1.

+

shared_ptr swap

+

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

+

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

+

Class shared_ptr example

+

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

+
+

Revised December 8, 1999

+

© 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.htm b/smart_ptr.htm new file mode 100644 index 0000000..ab95fc8 --- /dev/null +++ b/smart_ptr.htm @@ -0,0 +1,138 @@ + + + + + + +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.
scoped_arraySimple sole ownership of arrays.
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 throws exceptions.

+

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

+

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 Jul 2000

+

© 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..cd4d61b --- /dev/null +++ b/smart_ptr_test.cpp @@ -0,0 +1,263 @@ +// 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 +// 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. + +#include +#include +#include +#include +#include + +#ifdef NDEBUG +#error This test program makes no sense if NDEBUG is defined +#endif + +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 ) { assert( *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 + +// main --------------------------------------------------------------------// + +// This isn't a very systematic test; it just hits some of the basics. + +int main() { + + assert( UDT_use_count == 0 ); // reality check + + // test scoped_ptr with a built-in type + long * lp = new long; + scoped_ptr sp ( lp ); + assert( sp.get() == lp ); + assert( lp == sp.get() ); + assert( &*sp == lp ); + + *sp = 1234568901L; + assert( *sp == 1234568901L ); + assert( *lp == 1234568901L ); + ck( static_cast(sp.get()), 1234568901L ); + ck( lp, *sp ); + + sp.reset(); + assert( sp.get() == 0 ); + + // test scoped_ptr with a user defined type + scoped_ptr udt_sp ( new UDT( 999888777 ) ); + assert( udt_sp->value() == 999888777 ); + udt_sp.reset(); + udt_sp.reset( new UDT( 111222333 ) ); + assert( udt_sp->value() == 111222333 ); + udt_sp.reset( new UDT( 333222111 ) ); + assert( udt_sp->value() == 333222111 ); + + // test scoped_array with a build-in type + char * sap = new char [ 100 ]; + scoped_array sa ( sap ); + assert( sa.get() == sap ); + assert( sap == sa.get() ); + + strcpy( sa.get(), "Hot Dog with mustard and relish" ); + assert( strcmp( sa.get(), "Hot Dog with mustard and relish" ) == 0 ); + assert( strcmp( sap, "Hot Dog with mustard and relish" ) == 0 ); + + assert( sa[0] == 'H' ); + assert( sa[30] == 'h' ); + + sa[0] = 'N'; + sa[4] = 'd'; + assert( strcmp( sap, "Not dog with mustard and relish" ) == 0 ); + + sa.reset(); + assert( sa.get() == 0 ); + + // test shared_ptr with a built-in type + int * ip = new int; + shared_ptr cp ( ip ); + assert( ip == cp.get() ); + assert( cp.use_count() == 1 ); + + *cp = 54321; + assert( *cp == 54321 ); + assert( *ip == 54321 ); + ck( static_cast(cp.get()), 54321 ); + ck( static_cast(ip), *cp ); + + shared_ptr cp2 ( cp ); + assert( ip == cp2.get() ); + assert( cp.use_count() == 2 ); + assert( cp2.use_count() == 2 ); + + assert( *cp == 54321 ); + assert( *cp2 == 54321 ); + ck( static_cast(cp2.get()), 54321 ); + ck( static_cast(ip), *cp2 ); + + shared_ptr cp3 ( cp ); + assert( cp.use_count() == 3 ); + assert( cp2.use_count() == 3 ); + assert( cp3.use_count() == 3 ); + cp.reset(); + assert( cp2.use_count() == 2 ); + assert( cp3.use_count() == 2 ); + assert( cp.use_count() == 1 ); + cp.reset( new int ); + *cp = 98765; + assert( *cp == 98765 ); + *cp3 = 87654; + assert( *cp3 == 87654 ); + assert( *cp2 == 87654 ); + cp.swap( cp3 ); + assert( *cp == 87654 ); + assert( *cp2 == 87654 ); + assert( *cp3 == 98765 ); + cp.swap( cp3 ); + assert( *cp == 98765 ); + assert( *cp2 == 87654 ); + assert( *cp3 == 87654 ); + cp2 = cp2; + assert( cp2.use_count() == 2 ); + assert( *cp2 == 87654 ); + cp = cp2; + assert( cp2.use_count() == 3 ); + assert( *cp2 == 87654 ); + assert( cp.use_count() == 3 ); + assert( *cp == 87654 ); + + shared_ptr cp4; + swap( cp2, cp4 ); + assert( cp4.use_count() == 3 ); + assert( *cp4 == 87654 ); + assert( cp2.get() == 0 ); + +#ifndef BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION + set< shared_ptr > scp; + scp.insert(cp4); + assert( scp.find(cp4) != scp.end() ); + assert( 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 ); + assert( ca.get() == cap ); + assert( cap == ca.get() ); + assert( &ca[0] == cap ); + + strcpy( ca.get(), "Hot Dog with mustard and relish" ); + assert( strcmp( ca.get(), "Hot Dog with mustard and relish" ) == 0 ); + assert( strcmp( cap, "Hot Dog with mustard and relish" ) == 0 ); + + assert( ca[0] == 'H' ); + assert( ca[30] == 'h' ); + + shared_array ca2 ( ca ); + shared_array ca3 ( ca2 ); + + ca[0] = 'N'; + ca[4] = 'd'; + assert( strcmp( ca.get(), "Not dog with mustard and relish" ) == 0 ); + assert( strcmp( ca2.get(), "Not dog with mustard and relish" ) == 0 ); + assert( strcmp( ca3.get(), "Not dog with mustard and relish" ) == 0 ); + assert( ca.use_count() == 3 ); + assert( ca2.use_count() == 3 ); + assert( ca3.use_count() == 3 ); + ca2.reset(); + assert( ca.use_count() == 2 ); + assert( ca3.use_count() == 2 ); + assert( ca2.use_count() == 1 ); + + ca.reset(); + assert( ca.get() == 0 ); + + shared_array ca4; + swap( ca3, ca4 ); + assert( ca4.use_count() == 1 ); + assert( strcmp( ca4.get(), "Not dog with mustard and relish" ) == 0 ); + assert( ca3.get() == 0 ); + +#ifndef BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION + set< shared_array > sca; + sca.insert(ca4); + assert( sca.find(ca4) != sca.end() ); + assert( 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 ); + + assert( udta[0].value() == 111 ); + assert( udta[1].value() == 222 ); + assert( udta[2].value() == 333 ); + assert( udta2[0].value() == 111 ); + assert( udta2[1].value() == 222 ); + assert( udta2[2].value() == 333 ); + udta2.reset(); + assert( udta2.get() == 0 ); + assert( udta.use_count() == 1 ); + assert( udta2.use_count() == 1 ); + + assert( UDT_use_count == 4 ); // reality check + + // test shared_ptr with a user defined type + UDT * up = new UDT; + shared_ptr sup ( up ); + assert( up == sup.get() ); + assert( sup.use_count() == 1 ); + + sup->value( 54321 ) ; + assert( sup->value() == 54321 ); + assert( up->value() == 54321 ); + + shared_ptr sup2; + sup2 = sup; + assert( sup2->value() == 54321 ); + assert( sup.use_count() == 2 ); + assert( sup2.use_count() == 2 ); + sup2 = sup2; + assert( sup2->value() == 54321 ); + assert( sup.use_count() == 2 ); + assert( 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..dd976e4 --- /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 21 Feb 2000 +

+

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

+ +