|
|
|
Sponsored Link •
|
|
Advertisement
|
The ubiquitous reference to RAII (Resource Acquisition
Is Initialization [7])
should be justified in this case, because we come here seeking
the essence of what RAII is, namely that the constructor of a
local object handles the acquisition of a resource and its
destructor releases it. Using this idiom means that it isn't
possible to forget about releasing a resource; nor is it
required to think about the subtle issues that you've just
handled manually for the Example class. A simple wrapper
class, intended mainly for the purpose of adding RAII to
simple classes like SomeResource, might look like
this:
template <typename T> class RAII {
T* p_;
public:
explicit RAII(T* p) : p_(p) {}
~RAII() {
delete p_;
}
void reset(T* p) {
delete p_;
p_=p;
}
T* get() const {
return p_;
}
T& operator*() const {
return *p_;
}
void swap(RAII& other) {
std::swap(p_,other.p_);
}
private:
RAII(const RAII& other);
RAII& operator=(const RAII& other);
};
The only responsibilities this RAII class has are to store a
pointer, return it when someone needs it, and properly delete
it when destructed. Using such a class greatly simplifies the
Example class; both now and when applying any
future changes to it:
class Example {
RAII<SomeResource> p_;
RAII<SomeResource> p2_;
public:
Example() :
p_(new SomeResource()),
p2_(new SomeResource()) {}
Example(const Example& other)
: p_(new SomeResource(*other.p_)),
p2_(new SomeResource(*other.p2_)) {}
Example& operator=(const Example& other) {
// Self assignment?
if (this==&other)
return *this;
*p_=*other.p_;
*p2_=*other.p2_;
return *this;
}
~Example() {
std::cout << "Deleting Example, freeing SomeResource!\n";
}
};
You're basically back where you started with the original version, which hasn't a care with respect to exception safety. However, this time being oblivious to resource management and potential exceptions is intended, because it's already taken care of. This brings us to a very important point—the destructor now does nothing except write out a simple trace message:
~Example() {
std::cout << "Deleting Example, freeing SomeResource!\n";
}
This means that one could (or even should!) remove the
destructor, and rather rely on the compiler-generated version [8].
One of the Big Three is suddenly out of a job in the
Example class! However, you must duly note that
this simple example only handles raw pointers; there are many
other resources than that in real-world programs. Although
many of them provide clean-up services upon deletion (again,
RAII in action), some don't or can't. This, too, can be solved
without defining a destructor, which is the topic of the next
section.
Note: Diligent readers may observe that the
RAII class isn't exactly all it could be, but in
fact, it doesn't have to, because a similar implementation
already exists in the C++ Standard Library, namely
std::auto_ptr. It basically works the way we've
shown for the RAII class, only better. Why
provide your own, then? Because auto_ptr defines
a public copy constructor and copy assignment operator, both
of which assume ownership of the resource, whereas you need it
to be copied (the RAII class doesn't do that
either, but at least it reminds you to do it [9]). You
need to copy the resource, not have its ownership silently
transferred, so for the sake of this example we're happy to
reinvent the wheel.
What we've shown here in terms of resource management
exists in virtually every smart pointer class (many thousand
programmers think that the ones in Boost [10, 11] are especially fine
examples of smart pointers). But as mentioned, resource
management is not just about calling delete, it
may require some other logic, or other means of freeing the
resources (for example, calling close()). That's
probably the reason why more and more smart pointer types are
becoming smart resources; beyond supporting automatic deletion
of dynamically allocated resources, they allow customization
so that it is possible to have user-defined deleters called,
or even defining the deallocation function inline (made
possible through bind expressions and lambda expressions, as
those found in Boost.Lambda [12]). Much of the code that one
has previously put in the destructor of the aggregating class
is now coupled more tightly with the resource (or resource
holder), which makes perfect sense. It will be interesting to
see what the future brings in this area. With multi-threading
and exception-safety management that go far beyond what many
of us previously were exposed to (at least that's true for the
authors), intelligent resource management tools are becoming
increasingly important.
The Law of the Big Three has played, and continues to play, an important role in C++. However, we think that there is good reason to leave the destructor out of both the discussion and the implementation when possible, leading to a derivative "Law of the Big Two". The reason is that often, unadorned pointers should be avoided as class members—to be replaced by smart pointers. Either way, the role of copy constructors and copy assignment operators is often forgotten or ignored; it's our hope that this article may help address that in some small way.
boost::noncopyable is documented here.
const std::auto_ptr would have done the sa
me for the examples in this article.
Matthew Wilson is a software development consultant for Synesis Software, and creator of the STLSoft libraries. He is author of the forthcoming book Imperfect C++ (Addison-Wesley, 2004), and is currently working on his next two books, one of which is not about C++. Matthew can be contacted via http://imperfectcplusplus.com/.
Bjorn Karlsson is proud to be a C++ designer, programmer, teacher, preacher, and student. He has finally learned enough about C++ to realize how little he knows. When not reading or writing articles, books, or code, he has the privilege to be a part of the Boost community, and a member of The C++ Source Advisory Board. His book, Beyond The C++ Standard Library: An Introduction to Boost, will be published by Addison-Wesley in 2005. He appreciates it when people send him interesting emails at bjorn.karlsson@readsoft.com.
|
Sponsored Links
|