RAII
RAII is Resource Acquisition Is Initialization, and it’s a neat concept once you understand what it means and how it’s used. RAII was born out of C++ and invented by its creator Bjarne Stroustrup.
In C++, the only code that can be guaranteed to be executed after an exception is thrown are the destructors of objects residing on the stack. Resource management needs to be tied to the lifespan of suitable objects in order to gain automatic allocation and reclamation. Resources are acquired during initialization, and guaranteed to be released with the destruction of the same objects.
For local objects allocated on the stack, the languages scoping rules ensure that the destructor is called when its scope ends. Thus, by putting the resource release logic in the destructor, C++’s scoping provides direct support for RAII¹:
#include <iostream>
#include <chrono>
using namespace std::chrono;
class Timer {
system_clock::time_point start, finish;
public:
Timer() {
start = system_clock::now();
}
~Timer() {
finish = system_clock::now();
nanoseconds ns = finish - start;
std::cout << "Total time: "
<< ns.count() << " nanoseconds" << std::endl;
}
};
int main() {
Timer t;
// some long expensive calculation
}
In this snippet, the Timer class demonstrates using RAII to calculate and display
the time it takes for a portion of code to execute. A Timer object, t
, is
initialized in the first line of the main function, which will be used
to calculate and display the global time the program took to execute. Once some
long, expensive calculation runs, the main program will exit and the Timer object
will display the duration it took for the program to execute. Very handy for performance
debugging.
Smart Pointers
Consequently, this is how the C++11 library shared_ptr
works. It is a reference
counting smart pointer that shares ownership, so when the last copy of it goes out
of scope it will free the managed object. In case you’re wondering, unique_ptr
and
shared_ptr
live in the header <memory>
.
A smart pointer is a class that imitates raw pointers by overloading operators.
unique_ptr
is powered by rvalue references, a C++11 feature. While it is not copyable,
it is movable. With unique_ptr
and shared_ptr
, std::move
can be used as a
helper function that moves its resources into a different object. The main difference
between unique_ptr
and shared_ptr
is that unique_ptr does not have shared-ownership,
meaning there is only one reference available at a time²:
int main() {
std::unique_ptr<int> p(new int(42));
std::unique_ptr<int> p2 = std::move(p);
assert(p == nullptr);
std::cout << *p2;
}
One useful example of this is the pimpl
idiom, where you have a pointer
to some class that implements all of its methods. This is useful for breaking
out dependencies allowing your program to compile faster.
There is a smarter pointer available called shared_ptr
, originally developed
for the Boost library but now part of the C++11 standard. shared_ptr
objects
have shared ownership, meaining the last remaining owner of the pointer is responsible
for destroying the object, or otherwise releasing the resources associated with
the stored pointer³.
int main() {
std::shared_ptr<int> sp(new int(42));
std::shard_ptr<int> sp2(sp);
std::cout << *sp << " " << *sp2;
}