Reputation: 4928
With the advent of smart pointer in C++, is manually implementing RAII via constructors and destructors considered bad 'modern C++' practice? Or are there applications where this is still relevant?
Upvotes: 2
Views: 971
Reputation: 6607
This question is dangerously opinion based and may get closed. However I will try and give some facts and information to decide when manual RAII might be appropriate.
It is true that if we have all heap memory used in our class either allocated via a smart pointer or some container like a vector we can avoid explicitly creating destructors, and also avoid manually defining cumbersome methods such as copy constructors and assignment operators. This is a nice thing when it is possible but is not always appropriate. Now we will briefly discuss a few cases where this approach is on its own not flexible enough.
C code often allocates memory and other resources using functions provided by a library, it then usually provides functions to destroy those resources. In such a case we need to create some kind of container to represent those resources in RAII. This container inevitably does manual destruction etc.
It might technically be possible to achieve for some wrappers that return an appropriate pointer by creating a smart pointer with a custom destruction function as is possible. However this is then not substantially neater than just defining a class.
Not all resources a computer has are memory, and not all resources are appropriate for a smart pointer. We may need files, sockets, system wide mutexes and various other resource objects the OS provides.
Not all of these can nicely be represented in a smart pointer, and even if they are forced into using one with custom destruction functions it is likely uglier than making a proper class to wrap these resources.
Though some resources like memory are generally allocatable and releasable in any order. Some resources are not. For example if we are talking to a hardware device, we may represent the device as one resource and then features of it as separate ones.
It would in this case not be possible to conveniently use implicit RAII as we need to control the destruction order and do not want to leave it up to the user of our API to have to remember to destroy everything in the correct order. Instead it is neater to use a class with reference counting or some other way of internally tracking interlinked resources.
Often in the above, and other cases where manual implementation of RAII is needed. It can be easiest to create a simple object to wrap the resource and provide low level operations on it.
We can then have the higher level more complex and featurefull objects not having to also worry about managing memory, essentially separating that destruction and resource management code into its own unit. This takes away a lot of the pain of complicated destructors and such. Having an object use 10 resources is much easier if those resources have nice low level wrappers to manage their lifetime and low level functionality.
Upvotes: 4
Reputation: 41780
If you can use standard RAII wrapper, use them, don't reinvent the wheel. There are more than std::unique_ptr
, there are others, like std::lock_guard
.
In my codebase, I created a RAII wrapper for a binded opengl object. A binding can be a resource too!
In short, use standard RAII wrapper when you can. Implement yours otherwise. Use RAII as much as you like.
Upvotes: 3
Reputation: 303107
Memory, via allocation, isn't the only kind of Resource that can be Acquired, so pointers aren't the only kind of beast that RAII is for.
Consider, for instance, a scoped lock:
template <class Lockable>
class lock_guard {
Lockable& lck;
public:
lock_guard(Lockable& lck)
: lck(lck)
{
lck.lock();
}
~lock_guard()
{
lck.unlock()
}
};
No pointers. Still RAII. Still shiny, modern, and super useful.
Upvotes: 5