diff --git a/include/boost/smart_ptr.hpp b/include/boost/smart_ptr.hpp index 2111032..3dea3f9 100644 --- a/include/boost/smart_ptr.hpp +++ b/include/boost/smart_ptr.hpp @@ -9,6 +9,7 @@ // See http://www.boost.org for most recent version including documentation. // Revision History +// 21 May 01 Require complete type on delete (suggested by Vladimir Prus) // 21 May 01 operator= fails if operand transitively owned by *this, as in a // linked list (report by Ken Johnson, fix by Beman Dawes) // 21 Jan 01 Suppress some useless warnings with MSVC (David Abrahams) @@ -54,8 +55,9 @@ #include // for std::size_t #include // for std::auto_ptr #include // for std::swap -#include // for boost::noncopyable +#include // for boost::noncopyable, checked_delete, checked_array_delete #include // for std::less +#include // for BOOST_STATIC_ASSERT namespace boost { @@ -74,9 +76,8 @@ template class scoped_ptr : noncopyable { 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; } } + ~scoped_ptr() { checked_delete(ptr); } + void reset( T* p=0 ) { if ( ptr != p ) { checked_delete(ptr); ptr = p; } } T& operator*() const { return *ptr; } // never throws #ifdef BOOST_MSVC # pragma warning(push) @@ -107,9 +108,10 @@ template class scoped_array : noncopyable { typedef T element_type; explicit scoped_array( T* p=0 ) : ptr(p) {} // never throws - ~scoped_array() { delete [] ptr; } + ~scoped_array() { checked_array_delete(ptr); } - void reset( T* p=0 ) { if ( ptr != p ) {delete [] ptr; ptr=p;} } + void reset( T* p=0 ) { if ( ptr != p ) + {checked_array_delete(ptr); ptr=p;} } T* get() const { return ptr; } // never throws #ifdef BOOST_SMART_PTR_CONVERSION @@ -132,7 +134,7 @@ template class shared_ptr { explicit shared_ptr(T* p =0) : px(p) { try { pn = new long(1); } // fix: prevent leak if new throws - catch (...) { delete p; throw; } + catch (...) { checked_delete(p); throw; } } shared_ptr(const shared_ptr& r) : px(r.px) { ++*(pn = r.pn); } // never throws @@ -167,7 +169,7 @@ template class shared_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; } + if (*pn == 1) { checked_delete(px); } else { // allocate new reference counter long * tmp = new long(1); // may throw --*pn; // only decrement once danger of new throwing is past @@ -186,7 +188,7 @@ template class shared_ptr { shared_ptr& operator=(std::auto_ptr& r) { // code choice driven by guarantee of "no effect if new throws" - if (*pn == 1) { delete px; } + if (*pn == 1) { checked_delete(px); } else { // allocate new reference counter long * tmp = new long(1); // may throw --*pn; // only decrement once danger of new throwing is past @@ -200,12 +202,12 @@ template class shared_ptr { void reset(T* p=0) { if ( px == p ) return; // fix: self-assignment safe - if (--*pn == 0) { delete px; } + if (--*pn == 0) { checked_delete(px); } else { // allocate new reference counter try { pn = new long; } // fix: prevent leak if new throws catch (...) { ++*pn; // undo effect of --*pn above to meet effects guarantee - delete p; + checked_delete(p); throw; } // catch } // allocate new reference counter @@ -249,7 +251,7 @@ template class shared_ptr { template friend class shared_ptr; #endif - void dispose() { if (--*pn == 0) { delete px; delete pn; } } + void dispose() { if (--*pn == 0) { checked_delete(px); delete pn; } } void share(T* rpx, long* rpn) { if (pn != rpn) { // Q: why not px != rpx? A: fails when both == 0 @@ -282,7 +284,7 @@ template class shared_array { explicit shared_array(T* p =0) : px(p) { try { pn = new long(1); } // fix: prevent leak if new throws - catch (...) { delete [] p; throw; } + catch (...) { checked_array_delete(p); throw; } } shared_array(const shared_array& r) : px(r.px) // never throws @@ -303,12 +305,12 @@ template class shared_array { void reset(T* p=0) { if ( px == p ) return; // fix: self-assignment safe - if (--*pn == 0) { delete [] px; } + if (--*pn == 0) { checked_array_delete(px); } else { // allocate new reference counter try { pn = new long; } // fix: prevent leak if new throws catch (...) { ++*pn; // undo effect of --*pn above to meet effects guarantee - delete [] p; + checked_array_delete(p); throw; } // catch } // allocate new reference counter @@ -335,7 +337,7 @@ template class shared_array { T* px; // contained pointer long* pn; // ptr to reference counter - void dispose() { if (--*pn == 0) { delete [] px; delete pn; } } + void dispose() { if (--*pn == 0) { checked_array_delete(px); delete pn; } } }; // shared_array diff --git a/scoped_array.htm b/scoped_array.htm index e317e21..f146ee2 100644 --- a/scoped_array.htm +++ b/scoped_array.htm @@ -59,19 +59,27 @@ template<typename T> class scoped_array : Common Requirements.

scoped_array destructor

~scoped_array();
+

T is required be a complete type at point of instantiation.  See Common +Requirements.

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 )();
+

T is required be a complete type at point of instantiation.  See Common +Requirements.

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

+

T is required be a complete type at point of instantiation.  See Common +Requirements.

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, @@ -79,6 +87,8 @@ or if i is less than 0 or is greater or equal to the number of elements in the array.

scoped_array get

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

T is not required be a complete type at point of instantiation.  +See Common Requirements.

Returns the stored pointer.

Class scoped_array example

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

diff --git a/scoped_ptr.htm b/scoped_ptr.htm index 277b38e..4945084 100644 --- a/scoped_ptr.htm +++ b/scoped_ptr.htm @@ -59,25 +59,38 @@ template<typename T> class scoped_ptr : scoped_ptr constructors
explicit scoped_ptr( T* p=0 );  // never throws
+

T is not required be a complete type at point of instantiation.  +See Common Requirements.

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

+have been allocated via a C++ new expression or be 0.

scoped_ptr destructor

~scoped_ptr();
+

T is required be a complete type at point of instantiation.  See Common +Requirements.

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

T is required be a complete type at point of instantiation.  See Common +Requirements.

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
+

T is required be a complete type at point of instantiation.  See Common +Requirements.

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

scoped_ptr operator-> and get

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

T is required be a complete type at point of instantiation of +operator->().  See Common +Requirements.

+

T is not required be a complete type at point of instantiation of +get().  See Common Requirements.

Both return the stored pointer.

Class scoped_ptr examples

#include <iostream>
@@ -106,24 +119,16 @@ output:

Buckle my shoe

Handle/Body Idiom

-

One common usage of scoped_ptr 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.

+

One common usage of scoped_ptr is to implement a handle/body idiom which avoids exposing the body (implementation) in the header +file.

+

The scoped_ptr_example_test.cpp +sample program includes a header file, scoped_ptr_example.hpp, +which uses a scoped_ptr<> to an incomplete type to hide the +implementation.   The +instantiation of member functions which require a complete type occurs in the scoped_ptr_example.cpp +implementation file. 


-

Revised 21 May 2001

+

Revised 22 May 2001

© Copyright Greg Colvin and Beman Dawes 1999. Permission to copy, use, modify, sell and distribute this document is granted provided this copyright notice appears in all copies. This document is provided "as is" diff --git a/scoped_ptr_example.cpp b/scoped_ptr_example.cpp new file mode 100644 index 0000000..3e2e511 --- /dev/null +++ b/scoped_ptr_example.cpp @@ -0,0 +1,16 @@ +// Boost scoped_ptr_example implementation file -----------------------------// + +#include "scoped_ptr_example.hpp" +#include + +class example::implementation +{ + public: + ~implementation() { std::cout << "destroying implementation\n"; } +}; + +example::example() : _imp( new implementation ) {} + +void example::do_something() { std::cout << "did something\n"; } + +example::~example() {} diff --git a/scoped_ptr_example.hpp b/scoped_ptr_example.hpp new file mode 100644 index 0000000..97c8bff --- /dev/null +++ b/scoped_ptr_example.hpp @@ -0,0 +1,21 @@ +// Boost scoped_ptr_example header file ------------------------------------// + +#include + +// The point of this example is to prove that even though +// example::implementation is an incomplete type in translation units using +// this header, scoped_ptr< implementation > is still valid because the type +// is complete where it counts - in the inplementation translation unit where +// destruction is actually instantiated. + +class example : boost::noncopyable +{ + public: + example(); + ~example(); + void do_something(); + private: + class implementation; + boost::scoped_ptr< implementation > _imp; // hide implementation details +}; + diff --git a/scoped_ptr_example_test.cpp b/scoped_ptr_example_test.cpp new file mode 100644 index 0000000..3629ec8 --- /dev/null +++ b/scoped_ptr_example_test.cpp @@ -0,0 +1,10 @@ +// Boost scoped_ptr_example_test main program -------------------------------// + +#include "scoped_ptr_example.hpp" + +int main() +{ + example my_example; + my_example.do_something(); + return 0; +} \ No newline at end of file diff --git a/shared_array.htm b/shared_array.htm index 89d9339..24842d1 100644 --- a/shared_array.htm +++ b/shared_array.htm @@ -108,17 +108,23 @@ name BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION is defined.

Provides the type of the stored pointer.

shared_array constructors

explicit shared_array( T* p=0 );
+

T is required be a complete type at point of instantiation.  See Common +Requirements.

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
+

T is not required be a complete type at point of instantiation.  +See Common Requirements.

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();
+

T is required be a complete type at point of instantiation.  See Common +Requirements.

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 @@ -126,6 +132,8 @@ a value of 0 is harmless.

Does not throw exceptions.

shared_array operator=

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

T is required be a complete type at point of instantiation.  See Common +Requirements.

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 @@ -135,6 +143,8 @@ of the pointer stored in r. Afterwards, use_count()r.use_count()

shared_array reset

void reset( T* p=0 );
+

T is required be a complete type at point of instantiation.  See Common +Requirements.

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[] @@ -147,6 +157,8 @@ see ~shared_array).

an exception is thrown,  delete[] p is called.

shared_array operator[]

T& operator[](std::size_t i) const; // never throws

+

T is required be a complete type at point of instantiation.  See Common +Requirements.

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, @@ -154,16 +166,24 @@ or if i is less than 0 or is greater or equal to the number of elements in the array.

shared_array get

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

T is not required be a complete type at point of instantiation.  +See Common Requirements.

Returns the stored pointer.

shared_array use_count

long use_count() const; // never throws

+

T is not required be a complete type at point of instantiation.  +See Common Requirements.

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

shared_array unique

bool unique() const; // never throws

+

T is not required be a complete type at point of instantiation.  +See Common Requirements.

Returns use_count() == 1.

shared_array swap

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

+

T is not required be a complete type at point of instantiation.  +See Common Requirements.

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

Class shared_array example

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

diff --git a/shared_ptr.htm b/shared_ptr.htm index 4dd2b87..78ed702 100644 --- a/shared_ptr.htm +++ b/shared_ptr.htm @@ -118,6 +118,8 @@ the macro name BOOST_NO_MEMBER_TEMPLATES is defined.

Provides the type of the stored pointer.

shared_ptr constructors

explicit shared_ptr( T* p=0 );
+

T is required be a complete type at point of instantiation.  See Common +Requirements.

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

@@ -128,6 +130,8 @@ template<typename Y> shared_ptr(const shared_ptr<Y>& r); // never throws template<typename Y> shared_ptr(std::auto_ptr<Y>& r); +

T is not required be a complete type at point of instantiation.  +See Common Requirements.

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 @@ -138,6 +142,8 @@ is std::bad_alloc.   If an exception is thrown, that constructor has no effect.

shared_ptr destructor

~shared_ptr();
+

T is required be a complete type at point of instantiation.  See Common +Requirements.

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 @@ -149,6 +155,8 @@ template<typename Y> shared_ptr& operator=(const shared_ptr<Y>& r); template<typename Y> shared_ptr& operator=(std::auto_ptr<Y>& r); +

T is required be a complete type at point of instantiation.  See Common +Requirements.

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 @@ -164,6 +172,8 @@ is std::bad_alloc.   If an exception is thrown, the function has no effect.

shared_ptr reset

void reset( T* p=0 );
+

T is required be a complete type at point of instantiation.  See Common +Requirements.

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. 

@@ -176,33 +186,46 @@ on a pointer with a value of 0 is harmless.

an exception is thrown,  delete p is called.

shared_ptr operator*

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

T is required be a complete type at point of instantiation.  See Common +Requirements.

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

shared_ptr operator-> and get

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

T is required be a complete type at point of instantiation of +operator->().  See Common +Requirements.

+

T is not required be a complete type at point of instantiation of +get().  See Common Requirements.

Both return the stored pointer.

shared_ptr use_count

long use_count() const; // never throws

+

T is not required be a complete type at point of instantiation.  +See Common Requirements.

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

shared_ptr unique

bool unique() const; // never throws

+

T is not required be a complete type at point of instantiation.  +See Common Requirements.

Returns use_count() == 1.

shared_ptr swap

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

+

T is not required be a complete type at point of instantiation.  +See Common Requirements.

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

Class shared_ptr example

See shared_ptr_example.cpp for a complete example program.

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

Note that after the two containers have been populated, some of the FooPtr objects will have use_count()==1 rather than use_count()==2, since foo_set is a std::set -rather than a std::multiset.  Furthermore, use_count() may be even higher +rather than a std::multiset, and thus does not contain duplicate entries.  Furthermore, use_count() may be even higher at various times while push_back() and insert() container operations are performed.  More complicated yet, the container operations may throw exceptions under a variety of circumstances.  Without using a smart pointer, memory and exception management would be a nightmare.


-

Revised 21 May, 2001 +

Revised 22 May, 2001

© Copyright Greg Colvin and Beman Dawes 1999. Permission to copy, use, modify, sell and distribute this document is granted provided this copyright diff --git a/smart_ptr.htm b/smart_ptr.htm index dc5db1f..b7bcf04 100644 --- a/smart_ptr.htm +++ b/smart_ptr.htm @@ -27,11 +27,11 @@ provides four smart pointer template classes:

scoped_ptr - Simple sole ownership of single objects. + Simple sole ownership of single objects. Noncopyable. scoped_array - Simple sole ownership of arrays. + Simple sole ownership of arrays. Noncopyable. shared_ptr @@ -53,10 +53,25 @@ 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 +

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.

+for objects of type T throws exceptions.

+

T may be an incomplete type at the point of smart pointer +declaration.  Unless otherwise specified, it is required that T +be a complete type at point of instantiation of all member functions.

+

Rationale

+

The requirements on T are carefully crafted to ensure safety +yet allow handle-body (aka pimpl) and similar idioms.  In these idioms a +smart pointer may appear in translation units where T is an +incomplete type.  This separates interface from implementation and hides +implementation from translation units which merely use the interface.

+

Example

+

The scoped_ptr_example_test.cpp +sample program includes a header file, scoped_ptr_example.hpp, +which uses a scoped_ptr<> to an incomplete type.   The +instantiation of member functions which require a complete type occurs in the scoped_ptr_example.cpp +implementation file. 

Exception safety

Several functions in these smart pointer classes are specified as having "no effect" or "no effect except such-and-such" if an @@ -78,6 +93,10 @@ never throws.

Functions which destroy objects of the pointed to type are prohibited from throwing exceptions by the Common requirements.

History and acknowledgements

+

May, 2001. Vladimir Prus suggested requiring a complete type on +destruction.  Refinement evolved in discussions including Dave Abrahams, +Greg Colvin, Beman Dawes, Rainer Deyke, Peter Dimov, John Maddock, Vladimir Prus, +Shankar Sai, and others.

November, 1999. Darin Adler provided operator ==, operator !=, and std::swap and std::less specializations for shared types.

September, 1999. Luis Coelho provided shared_ptr::swap and shared_array::swap

@@ -125,7 +144,7 @@ implementation.

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


Revised  27 Jul 200022 May 2001

© Copyright Greg Colvin and Beman Dawes 1999. Permission to copy, use, modify, sell and distribute this document is granted provided this copyright