Most people think of std::unique_ptr
as a smart pointer type that helps manage
calls to new
and delete
in an RAII-safe manner. This is true, and most of
the time this is what I use std::unique_ptr
for. However something that I
think is less well known is that std::unique_ptr
can destruct the owned
pointer using a custom deleter type, which lets you dealloate the resource using
a mechanism other than the C++ delete
operator.
An idiom I find particularly handy is using std::unique_ptr
to automatically
calling std::fclose
on an owned FILE*
. Here's how you do it:
// Second argument is a pointer to the type of std::fclose, we could also have
// written it out explicitly as std::unique_ptr<FILE, int (*)(FILE*)>.
using FilePtr = std::unique_ptr<FILE, decltype(std::fclose) *>;
// Instantiate the FILE* with the destructor we want.
FilePtr file(std::fopen(filename, "rbe"), std::fclose);
// Do stuff with the file
std::fread(buf_.data(), 1, buf_.size(), file.get());
Now when the file
goes out of scope std::fclose()
will automatically be
called by the destructor.
You can also use this when interacting with C APIs that have init/destroy methods (which is probably most C APIs). For example, suppose you're using xxHash from C++. Normally you'd write code like:
XXH3_state_t *state = XX3_createState();
// Do stuff with state ...
XXH3_freeState(state);
If you want to do this safely you might be tempted to create a small wrapper
class or struct that manages a HHX3_state_t*
and calls XXH3_freeState
in the
destructor. But if this is all you want to do, you can also just use
std::unique_ptr
with a custom deleter:
using XXH3StatePtr = std::unique_ptr<XXH3_state_t, decltype(XX3_freeState) *>;
XXH3StatePtr state(XXH3_createState(), XXh3_freeState);
// Do stuff with state ...
If you're interfacing with a complicated C API that has a lot of methods it might be useful to create wrapper classes to provide a full OO API. But if you just want to do something really simple, this technique may be simpler.