There is support for such cases in shared_ptr tHRough what is called custom deleters.. To use a FILE* in a shared_ptr, we define a class that is responsible for deallocating the resource
Trang 1requires 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);
}
std::cout << "By now, the FILE has been closed!\n";
}
Note that to get the resource, we need to use the unpronounceable &* idiom, get,
or get_pointer on the shared_ptr (I clearly caution against using &* The choice between the other two is less clear.) The example could be made even
Trang 2simplerif we don't need to do more than call a single argument function when
deallocating, there's really no need to create a custom deleter class at all The
example could be rewritten as follows:
{
FILE* f=fopen("test.txt","r");
if (f==0) {
std::cout << "Unable to open file\n";
throw file_exception();
}
boost::shared_ptr<FILE> my_shared_file(f,&fclose);
// Position the file pointer
fseek(&*my_shared_file,42,SEEK_SET);
}
std::cout << "By now, the FILE* has been closed!\n";
Custom deleters are extremely useful for handling resources that need a special release procedure Because the deleter is not part of the shared_ptr type,
clients need not know anything about the resource that the smart pointer owns (besides how to use it, of course!) For example, a pool of objects can be used, and the custom deleter would simply return the object to the pool Or, a singleton
object could have a deleter that does nothing
Security Through Custom Deleters
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) {
Trang 3delete 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();
}
Note that we cannot use a free function as a factory for shared_ptr<A> here, because the nested deleter class is private to A Using this scheme, it isn't possible for users to create As on the stack, and it isn't possible to call delete using a pointer to A
Creating a shared_ptr from this
Sometimes, it is necessary to obtain a shared_ptr from thisthat is, you are making the assumption that your class is being managed by a shared_ptr, and you need a way to convert "yourself" into that shared_ptr Sounds like a
mission impossible? Well, the solution comes from a smart pointer component that we've yet to discussboost::weak_ptr A weak_ptr is an observer of
shared_ptrs; it just silently sits and watches them, but does not affect the
reference count By storing a weak_ptr to this as a member of the class, it's possible to retrieve a shared_ptr to this on demand To relieve you from the tedium of having to write the code for storing a weak_ptr to this and
subsequently obtain a shared_ptr from that weak_ptr, Boost.Smart_ptr provides a helper class for this task, called enable_shared_from_this Simply have your class derive publicly from enable_shared_from_this, and then use the function shared_from_this whenever you need to access the
Trang 4shared_ptr that is managing this Here's an example that demonstrates how enable_shared_from_this is used:
#include "boost/shared_ptr.hpp"
#include "boost/enable_shared_from_this.hpp"
class A;
void do_stuff(boost::shared_ptr<A> p) {
}
class A : public boost::enable_shared_from_this<A> {
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
Trang 5most 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 kept close to heart By using custom deleters, almost any type of resource can be stored in shared_ptrs This makes shared_ptr a general class for handling resource management, rather than "just" handling dynamically allocated objects There is a small overhead in size for shared_ptr compared to a raw pointer I have yet to see a case where this overhead actually matters so much that another solution must be sought Don't roll your own reference-counted smart pointer class Instead, use shared_ptrsmart pointers don't get much better than this
Use shared_ptr in the following scenarios:
When there are multiple clients of an object, but no explicit owner
When storing pointers in Standard Library containers
When passing objects to and from libraries without (other) expressed
ownership
When managing resources that need special cleanup[9]
[9]
With the help of custom deleters
shared_array
Header: "boost/shared_array.hpp"
shared_array is a smart pointer that enables shared ownership of arrays It is
to shared_ptr what scoped_array is to scoped_ptr shared_array differs from shared_ptr mainly in that it is used with arrays rather than a single object When we discussed scoped_array, I mentioned that std::vector was 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
Trang 6Because 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