I'm revisting an idea that was brought up a few days ago about a non-slicing smart-pointer.
It occurred to me that this might be a pretty useful addition to the STL, and so I was hoping that some of you here might be interested in helping to develop it into something that could eventually be submitted as a recommendation to the C++ standards commitee?
At any rate, all comments and suggestions are welcome. Here is the current implementation:
And a simple test program:Code:#include <memory > // for std::allocator #include <algorithm > // for std::swap /* A simple "non-slicing" memory manager. Note: NOT to be used to manage dynamically-allocated arrays (use std::vector or similar for arrays) */ template < typename Type, typename Allocator = std::allocator< Type > > class value_ptr { protected: class internal_allocator_base { public: virtual Type* clone( Type const* ) const = 0; virtual void destroy( Type* ) const = 0; }; template < typename Derived > class internal_allocator : public internal_allocator_base { public: virtual Type* clone( Type const* ptr ) const { if( ptr == 0 ) return 0; Derived* val = allocator.allocate( 1 ); try { allocator.construct( val, *static_cast< Derived const* >( ptr ) ); } catch( ... ) { allocator.deallocate( val, 1 ); throw; } return val; } virtual void destroy( Type* ptr ) const { if( ptr != 0 ) { Derived* tmp = static_cast< Derived* >( ptr ); try { allocator.destroy( tmp ); } catch( ... ) { allocator.deallocate( tmp, 1 ); throw; } allocator.deallocate( tmp, 1 ); } } static internal_allocator global; static typename Allocator::template rebind< Derived >::other allocator; }; public: template < typename Derived > value_ptr( Derived* ptr ) { init( ); reset( ptr ); } value_ptr( Type* ptr = 0 ) { init( ); reset( ptr ); } value_ptr( value_ptr const& rhs ) { init( ); reset( rhs ); } template < typename Derived > value_ptr& reset( Derived* ptr ) { internal_reset( ptr ); m_iab_ = &internal_allocator< Derived >::global; return *this; } template < typename Derived > inline value_ptr& operator = ( Derived* ptr ) { return reset( ptr ); } inline value_ptr& reset( Type* ptr = 0 ) { return reset< Type >( ptr ); } inline value_ptr& operator = ( Type* ptr ) { return reset( ptr ); } value_ptr& reset( value_ptr const& rhs ) { internal_reset( rhs.m_iab_->clone( rhs.m_ptr_ ) ); m_iab_ = rhs.m_iab_; return *this; } inline value_ptr& operator = ( value_ptr const& rhs ) { return reset( rhs ); } template < typename Derived > value_ptr& allocate( Derived const& val = Derived( ) ) { return reset ( static_cast< Derived* > ( internal_allocator< Derived >::global.clone( &val ) ) ); } inline value_ptr& allocate( void ) { return allocate< Type >( ); } friend void swap( value_ptr& lhs, value_ptr& rhs ) { std::swap( lhs.m_ptr_, rhs.m_ptr_ ); std::swap( lhs.m_iab_, rhs.m_iab_ ); } #ifdef VALUE_PTR_NO_IMPLICIT_CONVERSION inline Type& operator * ( void ) { return *m_ptr_; } inline Type const& operator * ( void ) const { return *m_ptr_; } #else inline operator Type* ( void ) { return m_ptr_; } inline operator Type const* ( void ) const { return m_ptr_; } #endif inline Type* operator -> ( void ) { return m_ptr_; } inline Type const* operator -> ( void ) const { return m_ptr_; } virtual ~value_ptr( void ) { reset( ); } protected: void init( void ) { m_ptr_ = 0; m_iab_ = &internal_allocator< Type >::global; } void internal_reset( Type* ptr ) { /* The temporary ensures that if the 'current' data's destructor throws an exception, the 'new' memory will still be cleaned up. */ Type* saved = m_ptr_; m_ptr_ = ptr; m_iab_->destroy( saved ); } Type* m_ptr_; internal_allocator_base* m_iab_; }; template < typename Type, typename Allocator > template < typename Derived > typename value_ptr< Type, Allocator >::template internal_allocator< Derived > value_ptr< Type, Allocator >::internal_allocator< Derived >::global = typename value_ptr< Type, Allocator >::template internal_allocator< Derived >( ); template < typename Type, typename Allocator > template < typename Derived > typename Allocator::template rebind< Derived >::other value_ptr< Type, Allocator >::internal_allocator< Derived >::allocator = typename Allocator::template rebind< Derived >::other( );
One thing I couldn't decide on was whether or not to give each instance a copy of it's allocator. It would certainly be a little more flexible (and more in line with other STL objects), but it just seemed so wasteful for a single object, so I've left it out for now.Code:#include <iostream> class base { public: void print( char const* msg ) { std::cout << reinterpret_cast< int* >( this ) << " : " << msg << std::endl; } base( void ) { print( "base::base( )" ); } base( base const& ) { print( "base::base( base const& )" ); } virtual void test( void ) { print( "base::test( )" ); } ~base( void ) { print( "base::~base( )" ); } }; class derived : public base { public: derived( void ) { print( "derived::derived( )" ); } derived( derived const& rhs ) : base( rhs ) { print( "derived::derived( derived const& )" ); } virtual void test( void ) { print( "derived::test( )" ); } ~derived( void ) { print( "derived::~derived( )" ); } }; int main( void ) { value_ptr< base > mb1 = new derived( ), mb2 = mb1; mb2->test( ); return 0; }



LinkBack URL
About LinkBacks




CornedBee