Presenting the Problem
A typical smart pointer wraps a raw pointer to a dynamically allocated object and overloads the operators
-> and
*. The responsibility for deleting the pointer is delegated to the smart pointer's destructor, thus freeing you from manualand bug-pronememory management. The problem, however, is that
auto_ptr, the only smart pointer class available in C++98, has peculiar copy and assignment semantics. When you assign an
auto_ptr x to another
auto_ptr y, not only is the target object modified (as expected) but so is the source object, which becomes null. This behavior makes
auto_ptr incompatible with STL containers and algorithms. Let's see how
shared_ptr solves this problem.
| Author's Note: To use shared_ptr you need to download its sources from Boost. Notice that this Solution adheres to the official TR1 Draft Proposal, which differs slightly from boost::shared_ptr. |
Construction and Initialization
shared_ptr's default constructor creates what is known as an empty smart pointer, i.e., an object whose pointer is null. You can assign a valid pointer to it later:
class Foo{
int x;
public:
explicit Foo(int val=0) :x(val) {}
void do_something() {/*..*/}
int show_x() const {std::cout<< x<<std::endl; }
};
#include <memory> //for shared_ptr
using std::tr1::shared_ptr;
int main()
{
shared_ptr<Foo> pf; //pf is an empty shared_ptr
//bind a "live" pointer to pf
pf=new Foo;
pf->do_mesomething(); //using shared_ptr's overloaded ->
(*pf).show_x();//using shared_ptr's overloaded *
}//pf's destructor called here, destroying Foo
shared_ptr defines a conversion operator to
bool. This is useful for testing whether the
shared_ptr is empty. To access the raw pointer directly, call
get():
if (pf) { //ensure that pf isn't empty before calling get
Foo *palias = pf.get();
}
The second
shared_ptr constructor takes a pointer to a dynamically allocated object and an optional deleter. The following example defines a deleter and passes its address to
shared_ptr's constructor:
void my_deleter(Foo *ptr)
{
delete ptr;
std::cout<< "using custom deleter" <<std::endl;
}
shared_ptr<Foo> pf (new Foo, my_deleter);
A deleter can be any callable entity: a free function, a
function object, etc. When a user-defined deleter
d is provided,
shared_ptr's destructor calls:
d(ptr);
Instead of deleting
ptr directly. A custom deleter enables you to bind
shared_ptr to a pointer that was allocated in a different DLL, for example.
Certain resources such as file descriptors, synchronization objects, and device contexts require that a special API function be called to release them. Using a custom deleter, shared_ptr can manage such resources elegantly. In the following example, shared_ptr holds a mutex pointer obtained from an API function. When the shared_ptr object is destroyed, the release_mutex() function is called automatically:
//API functions for acquiring and releasing a mutex
mutex_t* get_mutex(pid_t pid);
int release_mutex(mutex_t* pmx);
shared_ptr <mutex_t> pmutex (get_mutex(mypid),
release_mutex);