The smart pointer boost::shared_ptr
is similar to boost::scoped_ptr
. The key difference is that boost::shared_ptr
is not necessarily the exclusive owner of an object. Ownership can be shared with other smart pointers of type boost::shared_ptr
. In such a case, the shared object is not released until the last copy of the shared pointer referencing the object is destroyed. Because boost::shared_ptr
can share ownership, the smart pointer can be copied, which isn’t possible with boost::scoped_ptr
.
boost::shared_ptr
is defined in the header file boost/shared_ptr.hpp
.
boost::shared_ptr
#include <boost/shared_ptr.hpp>
#include <iostream>
int main()
{
boost::shared_ptr<int> p1{new int{1}};
std::cout << *p1 << '\n';
boost::shared_ptr<int> p2{p1};
p1.reset(new int{2});
std::cout << *p1.get() << '\n';
p1.reset();
std::cout << std::boolalpha << static_cast<bool>(p2) << '\n';
}
Example 1.3 uses two smart pointers, p1 and p2, of the type boost::shared_ptr
. p2 is initialized with p1 which means both smart pointers share ownership of the same int
object. When reset()
is called on p1, a new int
object is anchored in p1. This doesn’t mean that the existing int
object is destroyed. Since it is also anchored in p2, it continues to exist. After the call to reset()
, p1 is the sole owner of the int
object with the number 2 and p2 the sole owner of the int
object with the number 1.
boost::shared_ptr
uses a reference counter internally. Only when boost::shared_ptr
detects that the last copy of the smart pointer has been destroyed is the contained object released with delete
.
Like boost::scoped_ptr
, boost::shared_ptr
overloads operator bool()
, operator*()
, and operator->()
. The member functions get()
and reset()
are provided to retrieve the currently stored address or store a new one.
As a second parameter, a deleter can be passed to the constructor of boost::shared_ptr
. The deleter must be a function or function object that accepts as its sole parameter a pointer of the type boost::shared_ptr
was instantiated with. The deleter is called in the destructor instead of delete
. This makes it possible to manage resources other than dynamically allocated objects in a boost::shared_ptr
.
boost::shared_ptr
with a user-defined deleter#include <boost/shared_ptr.hpp>
#include <Windows.h>
int main()
{
boost::shared_ptr<void> handle(OpenProcess(PROCESS_SET_INFORMATION, FALSE,
GetCurrentProcessId()), CloseHandle);
}
In Example 1.4 boost::shared_ptr
is instantiated with void
. The first parameter passed to the constructor is the return value from OpenProcess()
. OpenProcess()
is a Windows function to get a handle to a process. In the example, OpenProcess()
returns a handle to the current process – to the example itself.
Windows uses handles to refer to resources. Once a resource isn’t used anymore, the handle must be closed with CloseHandle()
. The only parameter expected by CloseHandle()
is the handle to close. In the example, CloseHandle()
is passed as a second parameter to the constructor of boost::shared_ptr
. CloseHandle()
is the deleter for handle. When handle is destroyed at the end of main()
, the destructor calls CloseHandle()
to close the handle that was passed as a first parameter to the constructor.
Example 1.4 only works because a Windows handle is defined as void*
. If OpenProcess()
didn’t return a value of type void*
and if CloseHandle()
didn’t expect a parameter of type void*
, it wouldn’t be possible to use boost::shared_ptr
in this example. The deleter does not make boost::shared_ptr
a silver bullet to manage arbitrary resources.
boost::make_shared
#include <boost/make_shared.hpp>
#include <typeinfo>
#include <iostream>
int main()
{
auto p1 = boost::make_shared<int>(1);
std::cout << typeid(p1).name() << '\n';
auto p2 = boost::make_shared<int[]>(10);
std::cout << typeid(p2).name() << '\n';
}
Boost.SmartPointers provides a helper function boost::make_shared()
in boost/make_shared.hpp
. With boost::make_shared()
you can create a smart pointer of type boost::shared_ptr
without having to calling the constructor of boost::shared_ptr
yourself.
The advantage of boost::make_shared()
is that the memory for the object that has to be allocated dynamically and the memory for the reference counter used by the smart pointer internally can be reserved in one chunk. Using boost::make_shared()
is more efficient than calling new
to create a dynamically allocated object and calling new
again in the constructor of boost::shared_ptr
to allocate memory for the reference counter.
You can use boost::make_shared()
for arrays, too. With the second call to boost::make_shared()
in Example 1.5, an int
array with ten elements is anchored in p2.
boost::shared_ptr
has only supported arrays since Boost 1.53.0. boost::shared_array
provides a smart pointer that is analogous to boost::shared_ptr
in the same way that boost::scoped_array
is analogous to boost::scoped_ptr
. When built with Visual C++ 2013 and Boost 1.53.0 or newer, Example 1.5 prints class boost::shared_ptr<int [0]>
for p2.
Since Boost 1.53.0, boost::shared_ptr
supports single objects and arrays and detects whether it has to release resources with delete
or delete[]
. Because boost::shared_ptr
also overloads operator[]
(since Boost 1.53.0), this smart pointer is an alternative for boost::shared_array
.
boost::shared_array
#include <boost/shared_array.hpp>
#include <iostream>
int main()
{
boost::shared_array<int> p1{new int[1]};
{
boost::shared_array<int> p2{p1};
p2[0] = 1;
}
std::cout << p1[0] << '\n';
}
boost::shared_array
complements boost::shared_ptr
: Since boost::shared_array
calls delete[]
in the destructor, this smart pointer can be used for arrays. For versions older than Boost 1.53.0, boost::shared_array
had to be used for arrays because boost::shared_ptr
didn’t support arrays.
boost::shared_array
is defined in boost/shared_array.hpp
.
In Example 1.6, the smart pointers p1 and p2 share ownership of the dynamically allocated int
array. When the array in p2 is accessed with operator[]
to store the number 1, the same array is accessed with p1. Thus, the example writes 1
to standard output.
Like boost::shared_ptr
, boost::shared_array
uses a reference counter. The dynamically allocated array is not released when p2 is destroyed because p1 still contains a reference to that array. The array is only destroyed at the end of main()
when the scope ends for p1.
boost::shared_array
also provides the member functions get()
and reset()
. Furthermore, it overloads the operator operator bool
.
boost::shared_ptr
with BOOST_SP_USE_QUICK_ALLOCATOR
#define BOOST_SP_USE_QUICK_ALLOCATOR
#include <boost/shared_ptr.hpp>
#include <iostream>
#include <ctime>
int main()
{
boost::shared_ptr<int> p;
std::time_t then = std::time(nullptr);
for (int i = 0; i < 1000000; ++i)
p.reset(new int{i});
std::time_t now = std::time(nullptr);
std::cout << now - then << '\n';
}
It can make sense to prefer a smart pointer like boost::shared_ptr
over the ones from the standard library. Boost.SmartPointers supports macros to optimize the behavior of the smart pointers. Example 1.7 uses the macro BOOST_SP_USE_QUICK_ALLOCATOR
to activate an allocator shipped with Boost.SmartPointers. This allocator manages memory chunks to reduce the number of calls to new
and delete
for reference counters. The example calls std::time()
to measure the time before and after the loop. While the time it takes to execute the loop depends on the computer, the example may run faster with BOOST_SP_USE_QUICK_ALLOCATOR
than without. The documentation for Boost.SmartPointers doesn’t mention BOOST_SP_USE_QUICK_ALLOCATOR
. Thus, you should profile your program and compare the results you get with and without BOOST_SP_USE_QUICK_ALLOCATOR
.
In addition to BOOST_SP_USE_QUICK_ALLOCATOR
, Boost.SmartPointers supports macros like BOOST_SP_ENABLE_DEBUG_HOOKS
. The names of the macros start with BOOST_SP_ which makes it easy to search for them in the header files to get an overview on the available macros.