In C++11 is it possible to use shared_ptr
to control non-pointer resources?
It is possible to use unique_ptr
to manage non-pointer resources. This is done by implementing a custom deleter class which provides:
typedef {TYPE} pointer;
where {TYPE}
is the non-pointer resource typeoperator()(pointer)
which frees the controlled resource...and then instantiating a unique_ptr
with the custom deleter as the second template parameter.
For example, under Windows it is possible to create a unique_ptr
which manages a service control handle. This handle type is not freed by calling delete
, but by calling CloseServiceHandle()
. Here is sample code which does this:
struct SvcHandleDeleter { typedef SC_HANDLE pointer; SvcHandleDeleter() {}; template<class Other> SvcHandleDeleter(const Other&) {}; void operator()(pointer h) const { CloseServiceHandle(h); } }; typedef std::unique_ptr<SC_HANDLE,SvcHandleDeleter> unique_sch;
unique_sch scm(::OpenSCManagerA(0, 0, SC_MANAGER_ALL_ACCESS));
Is it possible to use shared_ptr
to control a non-pointer resource as well?
According to the documentation, there are shared_ptr
constructor overloads which take provide the means to provide a custom deleter class, but none of the constructors accept a resource type that is not either a pointer or a wrapper around a pointer.
How can this be done?
Sadly, shared_ptr
's need for type-erasure makes it impossible with the current interface to achieve exactly what you want. unique_ptr
manages to do that because it has static information on the actual deleter type, from where it can draw the actual "pointer" type. In shared_ptr
's case, the deleter type is lost in the type-erasure process (which is why you can't specify it in the shared_ptr
template).
Also note that unique_ptr
doesn't provide any converting constructors like shared_ptr
does (e.g. template<class Y> shared_ptr(Y* p)
). It can't do so because pointer
is not necessarily a real pointer type, and so it can't restrict what can be accepted (except maybe through some SFINAE with std::is_convertible_to
or something like that... but I digress).
Now, one obvious workaround is to simply new
the resource handle, as dumb as it sounds. :/
std::shared_ptr<SC_HANDLE> sp(new SC_HANDLE(::OpenSCManagerA(0, 0, SC_MANAGER_ALL_ACCESS)), [](SC_HANDLE* p){ ::CloseServiceHandle(*p); delete p; });
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With