diff --git a/index.htm b/index.htm new file mode 100644 index 0000000..04643fa --- /dev/null +++ b/index.htm @@ -0,0 +1,37 @@ + + +
+ +![]() |
+ Home | +Libraries | +People | +FAQ | +More | +
The header smart_ptr.h provides four smart pointer classes. Smart pointers ease +the management of memory dynamically allocated with C++ new expressions. + +
Revised July 23, 1999
+ + 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 @@ + + + +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.
+#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 + }; +}+
typedef T element_type;+
Provides the type of the stored pointer.
+explicit scoped_array( T* p=0 ); // never throws+
Constructs a scoped_array, storing a copy of p, which must +have been allocated via a C++ new[] expression or be 0.
+~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.
+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.
+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.
+T* get() const; // never throws+
Returns the stored pointer.
+[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 @@ + + + +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.
+#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 + }; +}+
typedef T element_type;+
Provides the type of the stored pointer.
+explicit scoped_ptr( T* p=0 ); // never throws+
Constructs a scoped_ptr, storing a copy of p, which must +have been allocated via a C++ new expression or be 0..
+~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.
+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.
+T& operator*() const; // never throws+
Returns a reference to the object pointed to by the stored pointer.
+T* operator->() const; // never throws +T* get() const; // never throws+
Both return the stored pointer.
+#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+
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..eebafdc --- /dev/null +++ b/shared_array.htm @@ -0,0 +1,180 @@ + + + +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_array +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.
+#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.
+typedef T element_type;+
Provides the type of the stored pointer.
+explicit shared_array( T* p=0 );+
Constructs a shared_array, storing a copy of p, +which must 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();+
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=( 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().
+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.
+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.
+T* get() const; // never throws+
Returns the stored pointer.
+long use_count() const; // never throws
+Returns the number of shared_arrays sharing ownership of the +stored pointer.
+bool unique() const; // never throws
+Returns use_count() == 1.
+void swap( shared_array<T>& other ) throw()
Swaps the two smart pointers, as if by std::swap.
+[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 @@ + + + +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.
+#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.
+typedef T element_type;+
Provides the type of the stored pointer.
+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();+
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=( 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.
+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.
+T& operator*() const; // never throws+
Returns a reference to the object pointed to by the stored pointer.
+T* operator->() const; // never throws +T* get() const; // never throws+
Both return the stored pointer.
+long use_count() const; // never throws
+Returns the number of shared_ptrs sharing ownership of the +stored pointer.
+bool unique() const; // never throws
+Returns use_count() == 1.
+void swap( shared_ptr<T>& other ) throw()
Swaps the two smart pointers, as if by std::swap.
+[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 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:
++ | +Simple sole ownership of single objects. | +
scoped_array | +Simple sole ownership of arrays. | +
shared_ptr | +Object ownership shared among multiple pointers | +
shared_array | +Array 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.
+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.
+Several functions in these smart pointer classes are specified as having +"no effect" or "no effect except such-and-such" if an +exception is thrown. This means that when an exception is thrown by +an object of one of these classes, the entire program state remains the same as +it was prior to the function call which resulted in the exception being +thrown. This amounts to a guarantee that there are no detectable side +effects. Other functions never throw exceptions. The only exception +ever thrown by functions which do throw (assuming T meets the Common +requirements) is std::bad_alloc, and that is thrown only by +functions which are explicitly documented as possibly throwing std::bad_alloc.
+Exception-specifications are not used; see exception-specification +rationale.
+All 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.
+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: +
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/smarttests.htm b/smarttests.htm new file mode 100644 index 0000000..dd976e4 --- /dev/null +++ b/smarttests.htm @@ -0,0 +1,540 @@ + + +In late January 2000, Mark Borgerding put forward a suggestion to boost for + a new design of smart pointer whereby an intrusive doubly linked list is used + to join together all instances of smart pointers sharing a given raw pointer. + This allowed avoidance of the costly heap allocation of a reference count that + occurred in the initial construction of the then current version of boost::shared_ptr. + Of course, nothing is for free and the benefit here was gained at the expense + of increased size and more costly copy operations. A debate ensued on the boost + mailing list and the tests which this page describes were performed to provide + a guide for current and future investigations into smart pointer implementation + strategies.
+Thanks are due to Dave Abrahams, + Gavin Collings, Greg Colvin and + Beman Dawes + for test code and trial implementations, the final version of which can be found + in .zip format here.
+Two tests were run: the first aimed to obtain timings for two basic individual + operations:
+The second attempted to gain more insight into normal usage by timing the fill + and sort algorithms for vectors and lists filled with the various smart pointers.
+Five smart pointer implementation strategies were tested:
+on two compilers:
+Additionally, generated pointer sizes (taking into account struct alignment) + were compared, as were generated code sizes for MSVC mainly by manual inspection + of generated assembly code - a necessity due to function inlining.
+All tests were run on a PII-200 running Windows NT version 4.0
+The following graphs show the overall time in nanoseconds to acquire a pointer + (default construction) perform n amortized copy operations on it and finally + release it. The initial allocation time for the contained pointer is not included, + although the time for it's deallocation is. The contained pointer pointed to + a trivial class, but for the inclusion of an intrusive reference count for the + benefit of the intrusive counted shared pointer. A dumb pointer (i.e. a smart + pointer that simply acquires and releases its contained pointer with no extra + overhead) and a raw pointer were also included for comparison.
++ | + | + |
+ | ![]() |
+ + |
+ | + | + |
+ | ![]() |
+ + |
+ | + | + |
+
Fitting straight lines to the above plots gives the following figures for initialization + and amortized copy operation for the two compilers (times in nanoseconds, errors + at two standard deviations) : -
++
+ + | +
+ initialization
+ |
+ copy operation | +
---|---|---|
+ simple counted
+ |
+ 3000 +/- 170 | +104 +/- 31 | +
+ special counted
+ |
+ 1330 +/- 50 | +85 +/- 9 | +
+ intrusive
+ |
+ 1000 +/- 20 | +71 +/- 3 | +
linked | +970 +/- 60 | +136 +/- 10 | +
cyclic | +1290 +/- 70 | +112 +/- 12 | +
dumb | +1020 +/- 20 | +10 +/- 4 | +
+ raw
+ |
+ 1038 +/- 30 | +10 +/- 5 | +
+ + | +
+ initialization
+ |
+ copy operation | +
---|---|---|
+ simple counted
+ |
+ 4620 +/- 150 | +301 +/- 28 | +
+ special counted
+ |
+ 1990 +/- 40 | +264 +/- 7 | +
+ intrusive
+ |
+ 1590 +/- 70 | +181 +/- 12 | +
linked | +1470 +/- 140 | +345 +/- 26 | +
cyclic | +2180 +/- 100 | +330 +/- 18 | +
dumb | +1590 +/- 70 | +74 +/- 12 | +
+ raw
+ |
+ 1430 +/- 60 | +27 +/- 11 | +
Note that the above times include a certain amount of loop overhead etc. for + each operation. An estimate of the pure smart pointer operation time 'overhead' + can be obtained by subtracting the dumb or raw figure from the smart pointer + time of interest.
+The test involved iterating a loop which creates raw pointers. These were then + shared among a varying number (set size) of smart pointers. A range of set sizes + was used and then a line fitted to get a linear relation with number of initializations + and copy-operations. A spreadsheet was used for the line fit, and to produce + the performance graphs above.
+To gain some insight in to operation within real life programs, this test was + devised. Smart pointers were used to fill standard containers which were then + sorted.
+In this case, the contained pointer pointed to a class which initializes a + private data member to a random value in its default constructor. This value + is used subsequently for the sort comparison test. The class also contains an + intrusive reference count for the benefit of the intrusive counted pointer.
+All times are in seconds for 300,000 contained pointers.
++ | vector | +list | +||
---|---|---|---|---|
+ + | +
+ fill
+ |
+ sort | +fill | +sort | +
+ simple counted
+ |
+ 46.54 | +2.44 | +47.09 | +3.22 | +
+ special counted
+ |
+ 14.02 | +2.83 | +7.28 | +3.21 | +
+ intrusive
+ |
+ 12.15 | +1.91 | +7.99 | +3.08 | +
linked | +12.46 | +2.32 | +8.14 | +3.27 | +
cyclic | +22.60 | +3.19 | +1.63 | +3.18 | +
+ raw
+ |
+ 11.81 | +0.24 | +27.51 | +0.77 | +
+
+ | vector | +list | +||
---|---|---|---|---|
+ + | +
+ fill
+ |
+ sort | +fill | +sort | +
+ simple counted
+ |
+ 1.83 | +2.37 | +1.86 | +4.85 | +
+ special counted
+ |
+ 1.04 | +2.35 | +1.38 | +4.58 | +
+ intrusive
+ |
+ 1.04 | +1.84 | +1.16 | +4.29 | +
linked | +1.08 | +2.00 | +1.21 | +4.33 | +
cyclic | +1.38 | +2.84 | +1.47 | +4.73 | +
+ raw
+ |
+ 0.67 | +0.28 | +1.24 | +1.81 | +
+
The following code sizes were determined by inspection of generated code for + MSVC only. Sizes are given in the form N / M / I where:
++
+ + | +
+ ptr()
+ |
+ ptr(p) | +ptr(ptr) | +op=() | +
+ ~ptr()
+ |
+
---|---|---|---|---|---|
+ simple counted
+ |
+ 38/110/O | +38/110/O | +9/23/I | +22/57/I | +17/40/I | +
+ special counted
+ |
+ 50/141/O | +50/141/O | +9/23/I | +23/64/I | +13/38/I | +
+ intrusive
+ |
+ 1/2/I | +3/6/I | +3/6/I | +6/11/I | +6/11/I | +
+ linked
+ |
+ 5/19/I | +5/15/I | +10/30/I | +27/59/I | +14/38/I | +
During the code inspection, a couple of minor points were noticed: -
+The following smart pointer sizes were obtained in bytes
++ + | +
+ MSVC
+ |
+
+ GCC
+ |
+
---|---|---|
+ simple counted
+ |
+
+ 8
+ |
+
+ 8
+ |
+
+ special counted
+ |
+
+ 8
+ |
+
+ 12
+ |
+
+ intrusive
+ |
+
+ 4
+ |
+
+ 4
+ |
+
+ linked
+ |
+
+ 12
+ |
+
+ 12
+ |
+
+ cyclic
+ |
+
+ 8
+ |
+
+ 8
+ |
+
The timing results mainly speak for themselves: clearly an intrusive pointer + outperforms all others and a simple heap based counted pointer has poor performance + relative to other implementations. The selection of an optimal non-intrusive + smart pointer implementation is more application dependent, however. Where small + numbers of copies are expected, it is likely that the linked implementation + will be favoured. Conversely, for larger numbers of copies a counted pointer + with some type of special purpose allocator looks like a win. Other factors + to bear in mind are: -
+Revised 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.
+ +