Reputation: 47
Is there an easy way of freeing all the memory when I am exiting due to an error in the program? I don't want to free every single allocation, as it's hard to keep track of all of them. And how do the experienced developers that work with C++ approach this problem?
Upvotes: 0
Views: 630
Reputation: 275660
RAII is a technique where all resources are owned by objects that have destructors, except objects that exist directly in automatic (stack) and static/global storage.
When an exception occurs that isn't unhandled, C++ guarantees your program will unroll the stack, destroying each object on the stack in turn.
When you exit main, C++ guarantees that objects of static/global storage duration get destroyed as well.
Done properly, every resource you allocated is cleaned up.
This requires some discipline, and is often worth it.
Now, you can rely on the fact that modern OS's recycle memory and file handle resources that your program acquires while running, and just exit your program.
This is a lot easier. But it has serious limitations.
Not every program has resources that are cleaned up by an OS. If you are negotiating with a server for some resource, your OS cleanup won't work. If you create some kinds of a named pipes, your OS cleanup won't work. If you make some kinds of temporary files, your OS cleanup won't work.
In addition, exiting out of part of a program is a lot like exiting the program as a whole. So proper RAII cleanup in each part results in entire programs that clean themselves up when they shut down.
RAII is "resource allocation is initialization". The simplest case is a std::unique_ptr<T>
, where you do this:
std::unique_ptr<int> pint = std::make_unique<int>(3);
now pint
has a pointer to the allocated int
whose value is 3
. Its ownership can be moved to another unique_ptr
(or even a shared_ptr
), and when the smart pointer that owns the memory goes out of scope, the memory is recycled.
This could happen when object containing pint
is destroyed, or when pint
goes out of scope as it was an automatic storage variable (on the stack), or when an exception is thrown past the destruction of pint
.
Fancier versions of this require writing your own destructors.
One good tool to upgrade C-style resource management to C++-style RAII is a scope_guard
, that runs arbitrary code on destruction. (Be sure that destruction code cannot throw).
template<class F>
struct scope_guard {
F f;
bool bDoIt = true;
scope_gaurd( F in ):f(std::move(in)) {}
~scope_guard() { run_now(); }
void skip() { bDoIt = false; }
bool run_now() {
if (!bDoIt) return false;
f();
bDoIt = false;
return true;
}
scope_guard(scope_guard const&)=delete;
scope_guard(scope_guard && o):f(std::move(o.f)), bDoIt(o.bDoIt) {
o.bDoIt = false;
}
};
using storable_scope_guard = scope_guard<std::function<void()>>;
then you can do this:
auto atexit0 = scope_guard{ []{ /* recycle resources you just allocated */ } };
in your function body right after you allocate some resources that need cleaning up (as opposed to the C-style "do it manually at the end of the function, possibly with a bunch of gotos").
Upvotes: 4