In practice with C++, what is RAII, what are smart pointers, how are these implemented in a program and what are the benefits of using RAII with smart pointers?
RAII This is a strange name for a simple but awesome concept. Better is the name Scope Bound Resource Management (SBRM). The idea is that often you happen to allocate resources at the begin of a block, and need to release it at the exit of a block. Exiting the block can happen by normal flow control, jumping out of it, and even by an exception. To cover all these cases, the code becomes more complicated and redundant.
Just an example doing it without SBRM:
void o_really() {
resource * r = allocate_resource();
try {
// something, which could throw. ...
} catch(...) {
deallocate_resource(r);
throw;
}
if(...) { return; } // oops, forgot to deallocate
deallocate_resource(r);
}
As you see there are many ways we can get pwned. The idea is that we encapsulate the resource management into a class. Initialization of its object acquires the resource ("Resource Acquisition Is Initialization"). At the time we exit the block (block scope), the resource is freed again.
struct resource_holder {
resource_holder() {
r = allocate_resource();
}
~resource_holder() {
deallocate_resource(r);
}
resource * r;
};
void o_really() {
resource_holder r;
// something, which could throw. ...
if(...) { return; }
}
That is nice if you have got classes of their own which are not solely for the purpose of allocating/deallocating resources. Allocation would just be an additional concern to get their job done. But as soon as you just want to allocate/deallocate resources, the above becomes unhandy. You have to write a wrapping class for every sort of resource you acquire. To ease that, smart pointers allow you to automate that process:
shared_ptr create_entry(Parameters p) {
shared_ptr e(Entry::createEntry(p), &Entry::freeEntry);
return e;
}
Normally, smart pointers are thin wrappers around new / delete that just happen to call delete when the resource they own goes out of scope. Some smart pointers, like shared_ptr allow you to tell them a so-called deleter, which is used instead of delete. That allows you, for instance, to manage window handles, regular expression resources and other arbitrary stuff, as long as you tell shared_ptr about the right deleter.
There are different smart pointers for different purposes:
Code:
unique_ptr p(new plot_src); // now, p owns
unique_ptr u(move(p)); // now, u owns, p owns nothing.
unique_ptr v(u); // error, trying to copy u
vector> pv;
pv.emplace_back(new plot_src);
pv.emplace_back(new plot_src);
Unlike auto_ptr, unique_ptr can be put into a container, because containers will be able to hold non-copyable (but movable) types, like streams and unique_ptr too.
Code:
void do_something() {
scoped_ptr sp(new pipe);
// do something here...
} // when going out of scope, sp will delete the pointer automatically.
Code:
shared_ptr p(new plot_src(&fx));
plot1->add(p)->setColor("#00FF00");
plot2->add(p)->setColor("#FF0000");
// if p now goes out of scope, the src won't be freed, as both plot1 and
// plot2 both still have references.
As you see, the plot-source (function fx) is shared, but each one has a separate entry, on which we set the color. There is a weak_ptr class which is used when code needs to refer to the resource owned by a smart pointer, but doesn't need to own the resource. Instead of passing a raw pointer, you should then create a weak_ptr. It will throw an exception when it notices you try to access the resource by an weak_ptr access path, even though there is no shared_ptr anymore owning the resource.