Boost shared_ptr: how to use custom removers and distributors

The free allocate_shared function can be used with any standard compatible allocator. But what about the shared_ptr and reset constructor.

template<class Y, class D, class A> shared_ptr(Y * p, D d, A a); template<class Y, class D, class A> void reset(Y * p, D d, A a); 

The manual states that D must provide a call statement that will be used to delete the pointer, and A must be a standard compatible dispenser. If so, why do we need D ? Can't A perform both distribution and delegation? Don't you think that the requirement to provide a debit for each custom dispenser makes these methods almost useless? When I use custom allocators, I switch to allocate_shared . How to find out what is the right way to free memory allocated by a user allocator?

EDIT: After some experimentation with the verbatim allocator and deletion, I realized that the distributor was passed to the shared_ptr constructor and the factory > allocate_shared function is used to extract the internal shared_ptr structure. allocate_shared never uses the passed allocator to distribute the shared object. I think the acceleration guide could explain how the distributor is used more explicitly.

+3
source share
1 answer

A dispenser is designed to allocate and free internal shared_ptr parts, not an object.

That is, while the deleter gives us full control over our shared object (because we control how it was received and released), the dispenser parameter gives us control over the internal details of our general nature of the objects.

If you look at N2351 , then at the end of the dispenser offer, they note that Boost has implemented this function and refers to an example that was demonstrated for its use.

Here is an example, verbatim:

 #include <boost/config.hpp> // shared_ptr_alloc2_test.cpp // // Copyright (c) 2005 Peter Dimov // // Distributed under the Boost Software License, Version 1.0. (See // accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #include <boost/detail/lightweight_test.hpp> #include <boost/shared_ptr.hpp> #include <memory> #include <cstddef> // test_allocator struct test_allocator_base { int id_; static int last_global_id_; static int count_; explicit test_allocator_base( int id ): id_( id ) { } }; int test_allocator_base::last_global_id_ = 0; int test_allocator_base::count_ = 0; template<class T> class test_allocator: public test_allocator_base { public: typedef T * pointer; typedef T const * const_pointer; typedef T & reference; typedef T const & const_reference; typedef T value_type; typedef std::size_t size_type; typedef std::ptrdiff_t difference_type; private: static T * last_pointer_; static std::size_t last_n_; static int last_id_; public: template<class U> struct rebind { typedef test_allocator<U> other; }; pointer address( reference r ) const { return &r; } const_pointer address( const_reference s ) const { return &s; } explicit test_allocator( int id = 0 ): test_allocator_base( id ) { } template<class U> test_allocator( test_allocator<U> const & r ): test_allocator_base( r ) { } template<class U> test_allocator & operator=( test_allocator<U> const & r ) { test_allocator_base::operator=( r ); return *this; } void deallocate( pointer p, size_type n ) { BOOST_TEST( p == last_pointer_ ); BOOST_TEST( n == last_n_ ); BOOST_TEST( id_ == last_id_ ); --count_; ::operator delete( p ); } pointer allocate( size_type n, void const * ) { T * p = static_cast< T* >( ::operator new( n * sizeof( T ) ) ); last_pointer_ = p; last_n_ = n; last_id_ = id_; last_global_id_ = id_; ++count_; return p; } void construct( pointer p, T const & t ) { new( p ) T( t ); } void destroy( pointer p ) { p->~T(); } size_type max_size() const { return size_type( -1 ) / sizeof( T ); } }; template<class T> T * test_allocator<T>::last_pointer_ = 0; template<class T> std::size_t test_allocator<T>::last_n_ = 0; template<class T> int test_allocator<T>::last_id_ = 0; template<class T, class U> inline bool operator==( test_allocator<T> const & a1, test_allocator<U> const & a2 ) { return a1.id_ == a2.id_; } template<class T, class U> inline bool operator!=( test_allocator<T> const & a1, test_allocator<U> const & a2 ) { return a1.id_ != a2.id_; } template<> class test_allocator<void>: public test_allocator_base { public: typedef void * pointer; typedef void const * const_pointer; typedef void value_type; template<class U> struct rebind { typedef test_allocator<U> other; }; explicit test_allocator( int id = 0 ): test_allocator_base( id ) { } template<class U> test_allocator( test_allocator<U> const & r ): test_allocator_base( r ) { } template<class U> test_allocator & operator=( test_allocator<U> const & r ) { test_allocator_base::operator=( r ); return *this; } }; // struct X { static int instances; X() { ++instances; } ~X() { --instances; } private: X( X const & ); X & operator=( X const & ); }; int X::instances = 0; int main() { BOOST_TEST( X::instances == 0 ); boost::shared_ptr<void> pv( new X, boost::checked_deleter<X>(), std::allocator<X>() ); BOOST_TEST( X::instances == 1 ); pv.reset( new X, boost::checked_deleter<X>(), test_allocator<float>( 42 ) ); BOOST_TEST( X::instances == 1 ); BOOST_TEST( test_allocator_base::last_global_id_ == 42 ); BOOST_TEST( test_allocator_base::count_ > 0 ); pv.reset(); BOOST_TEST( X::instances == 0 ); BOOST_TEST( test_allocator_base::count_ == 0 ); pv.reset( new X, boost::checked_deleter<X>(), test_allocator<void>( 43 ) ); BOOST_TEST( X::instances == 1 ); BOOST_TEST( test_allocator_base::last_global_id_ == 43 ); pv.reset( new X, boost::checked_deleter<X>(), std::allocator<void>() ); BOOST_TEST( X::instances == 1 ); pv.reset(); BOOST_TEST( X::instances == 0 ); return boost::report_errors(); } 
+6
source

Source: https://habr.com/ru/post/1398139/


All Articles