I am trying to understand how boost::make_shared
does the memory allocation for the object managed by a boost::shared_ptr
and the reference-counting object (the shared_ptr
uses) together.
The make_shared
function begins execution here:
template< class T, class A1, class A2, class A3 >
typename boost::detail::sp_if_not_array< T >::type make_shared( A1 && a1, A2 && a2, A3 && a3 )
{
//Seems to create the smart_ptr for the object
boost::shared_ptr< T > pt( static_cast< T* >( 0 ), BOOST_SP_MSD( T ) );
//Not sure?
boost::detail::sp_ms_deleter< T > * pd = static_cast<boost::detail::sp_ms_deleter< T > *>( pt._internal_get_untyped_deleter() );
//Calculates the address at which the bulk-allocation begins
void * pv = pd->address();
//Allocates the memory at address pv?
::new( pv ) T(
boost::detail::sp_forward<A1>( a1 ),
boost::detail::sp_forward<A2>( a2 ),
boost::detail::sp_forward<A3>( a3 )
);
//Not sure
pd->set_initialized();
//Not sure
T * pt2 = static_cast< T* >( pv );
//Not sure
boost::detail::sp_enable_shared_from_this( &pt, pt2, pt2 );
return boost::shared_ptr< T >( pt, pt2 );
}
Would somebody be able to help explain the remaining lines?
I am trying to identify where the size of the bulk memory allocation (the object being pointed to and the shared_ptr reference counting object) is determined?
What's throwing me is that the call to address()
doesn't seem to consider the size of the T
object when allocating the address to allocate at.
(I really don't get what the three AX
parameters are which enter the method and are passed to the placement new()
call)
Let's take it line by line
boost::shared_ptr< T > pt( static_cast< T* >( 0 ), BOOST_SP_MSD( T ) );
This creates the shared pointer, but the key here is the deleter. The BOOST_SP_MSD(T)
is a macro which resolves to a deleter with enough extra space for your data. So the shared pointer reference count block also includes the space for the deleter, which now includes the space for your T
//Not sure?
boost::detail::sp_ms_deleter< T > * pd = static_cast<boost::detail::sp_ms_deleter< T > *>( pt._internal_get_untyped_deleter() );
This gets the deleter address from the shared pointer. This will point to the deleter created above.
//Calculates the address at which the bulk-allocation begins
void * pv = pd->address();
This returns the starting address of for the type T
, which is currently uninitialized and is part of the deleter
//Allocates the memory at address pv?
::new( pv ) T(
boost::detail::sp_forward<A1>( a1 ),
boost::detail::sp_forward<A2>( a2 ),
boost::detail::sp_forward<A3>( a3 )
);
This is a placement-new. This constructs your T
at the address provided in pv
. It's passing 3 arguments because this is the 3-arg version of make_shared
.
//Not sure
pd->set_initialized();
This is an internal flag in the deleter, which lets it know that the T
has been constructed (so that when the deleter's operator()
is called, it will destroy it)
//Not sure
T * pt2 = static_cast< T* >( pv );
This casts the void* above into the T*
. Honestly, I'm not sure why they just didn't retain the result of the placement new.
//Not sure
boost::detail::sp_enable_shared_from_this( &pt, pt2, pt2 );
This is necessary to provide the enable_shared_from_this
functionality. This internal function sets up the underlying mechanism for enable_shared_from_this
. It's normally called when you place something into the shared_ptr
return boost::shared_ptr< T >( pt, pt2 );
This actually creates a new boost::shared_ptr
that uses the same reference counted region as the pt
, but its get()
and related methods will return pt2
, which is the pointer to T
, which is stored in the deleter.