Search code examples
c++c++11raii

Is implementing RAII via constructors and destructors considered bad 'Modern C++'?


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?


Solution

  • 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.

    When we can Avoid -Manual- RAII

    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.

    When we Cannot Easily Avoid Manual RAII

    Wrappers around C code

    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.

    Non Memory Resources

    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.

    Resources with Interdependent Destruction

    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.

    The Single Responsibility Principle

    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.