requires other cleanup than a simple delete. There is support for such cases in
shared_ptr tHRough what is called custom deleters. Resource handles, such as
FILE*, or operating systemspecific handles, are typically released through an
operation such as fclose. To use a FILE* in a shared_ptr
, we define a class
that is responsible for deallocating the resource.
class FileCloser {
public:
void operator()(FILE* file) {
std::cout << "The FileCloser has been called with a FILE*, "
"which will now be closed.\n";
if (file!=0)
fclose(file);
}
};
This is the function object that we'll use to make sure that fclose is called when
the resource should be released. Here's an example program that utilizes our
FileCloser class.
int main() {
std::cout <<
"shared_ptr example with a custom deallocator.\n";
{
FILE* f=fopen("test.txt","r");
if (f==0) {
std::cout << "Unable to open file\n";
throw "Unable to open file";
}
boost::shared_ptr<FILE>
my_shared_file(f, FileCloser());
// Position the file pointer
fseek(my_shared_file.get(),42,SEEK_SET);
We've already seen how using a protected destructor in a base class helps add
safety to classes used with shared_ptr. Another way of achieving the same
level of safety is to declare the destructor protected (or private) and use a custom
deleter to take care of destroying the object. This custom deleter must be made a
friend of the class that it is to delete for this to work. A nice way to encapsulate this
deleter is to implement it as a private nested class, like the following example
demonstrates:
#include "boost/shared_ptr.hpp"
#include <iostream>
class A {
class deleter {
public:
void operator()(A* p) {
delete p;
}
};
friend class deleter;
public:
virtual void sing() {
std::cout << "Lalalalalalalalalalala";
}
static boost::shared_ptr<A> createA() {
boost::shared_ptr<A> p(new A(),A::deleter());
return p;
}
protected:
virtual ~A() {};
};
int main() {
boost::shared_ptr<A> p=A::createA();
public:
void call_do_stuff() {
do_stuff(shared_from_this());
}
};
int main() {
boost::shared_ptr<A> p(new A());
p->call_do_stuff();
}
The example also demonstrates a case where you need the shared_ptr that is
managing this. Class A has a member function call_do_stuff that needs to
call the free function do_stuff, which expects an argument of type boost::
shared_ptr<A>. Now, in A::call_do_stuff, this is simply a pointer to
A
, but because A derives from enable_shared_from_this, calling
shared_from_this returns the shared_ptr that we're seeking. In
shared_from_this, which is a member of enable_shared_from_this,
the internally stored weak_ptr is converted to a shared_ptr, thereby
increasing the reference count to make sure that the object is not deleted.
Summary
Reference-counted smart pointers are extremely important tools. Boost's
shared_ptr provides a solid and flexible solution that is proven through
extensive use in many environments and circumstances. It is common to need to
share objects among clients, and that often means that there is no way of telling if,
and when, the object can be deleted safely. shared_ptr insulates clients from
knowing about what other objects are using a shared object, and relieves them of
the task of releasing the resource when no objects refer to it. This is arguably the
most important of the smart pointer classes in Boost. You should get acquainted
with the other classes in Boost.Smart_ptr, too, but this one should definitely be
often a better choice. But shared_array adds some value over vector,
because it offers shared ownership of arrays. The shared_array interface is
similar to that of shared_ptr, but with the addition of a subscript operator and
without support for custom deleters.
Because a shared_ptr to std::vector offers much more flexibility than
shared_array, there's no usage section on shared_array in this chapter. If you find
that you need boost::shared_array, refer to the online documentation.