VOOZH about

URL: https://www.geeksforgeeks.org/cpp/resource-acquisition-is-initialization/

⇱ Resource Acquisition Is Initialization - GeeksforGeeks


  • Courses
  • Tutorials
  • Interview Prep

Resource Acquisition Is Initialization

Last Updated : 24 Jan, 2026

RAII stands for "Resource Acquisition Is Initialization". Suppose there is a  “resource” in terms of Files, Memory, Sockets, etc. RAII means that an object’s creation and destruction are tied to a resource being acquired and released.

Let's assume we have to write events to a log file. A non-object-oriented way to do this would be:

We forgot to close log_fd. It's imperative that every Linux process closes its file descriptors during its lifetime; otherwise, it may run out of file descriptors and misbehave/fail. Additionally, leaving file descriptors open will cause the kernel to keep extra bookkeeping and state around for unused yet open file descriptors and their backing file objects, resulting in increased memory consumption and unnecessary work.

An Object-Oriented Approach

Consider a Logger class that writes events to a log file. In order to write to the file, it will need to open a file descriptor to it. Let's take a look at how a primitive version of this class might look:


Now let's see how we will use this API:


The good thing is that upon destruction, the `Logger` closes the underlying file descriptor, making it responsible for its own resources.

However, the `logger` object is in limbo between the `Constructor` and the `Initialize` call, as it has no file to write to internally. Relying on "good and responsible" users to use our API judiciously is always a bad code smell.

Let's see if we can do better:

RAII To The Rescue

Putting RAII into practice, let's acquire the file in the `Logger` constructor itself. Here's how it would look:

This version improves our previous API by acquiring the file in the constructor. It still has one flaw but let’s see how we use this API:

If we fail to open the log file, then all logging events will fail. We could have `Log` return an error, but the client may keep retrying. To the client, the object creation succeeded, implying that the `Logger` is ready to log events. Any `Log` errors may be transient and worth retrying.

However, this is not the case. All `Log` calls will fail. We should have returned an error where it happened – in the constructor. Constructors, unfortunately, cannot return errors. Let's see an elegant way to get around this.

RAII With Acquisition Error Handling

Let's return a `std::unique_ptr` from a static constructor. Internally, this will create a `Logger` object on the heap, wrap it in a `unique_ptr`, and return it to the client. If we fail to open the log file, we will return a `nullptr` instead. This way, we will inform the client that we failed when we actually failed.

Let's see how we can use this API:


Now that the client knows when a `Logger` creation fails, it can take necessary steps without being confused by `Log` API failures. When `logger` goes out of scope, its destructor is called, and the log file is closed.

Other Scenarios where RAII can be used

1. To automatically join a thread:

When we create a thread in C++, we have to either join() or detach() the thread. In case some exception occurs in the function, and we are not able to join() or detach() the thread, the thread will become a zombie thread. To avoid this, we can use RAII:


Output: Exception Unhandled.

In this program, we are calling func1() inside a try-catch block in main(). If func1() throws an exception, we'll catch the exception in main().

Now, func1() is called, and it creates a thread t1. Then it does some processing and expects to join t1 after the processing is done. But unfortunately, some exception occurs during this process, and func1 rethrows the exception in the catch block, without joining t1. He hopes to catch the exception in main(), but the program will throw an error because t1 has not been joined.

In a deeper calling stack, we can get trapped in this error quite easily. One way to solve this is to create a Wrapper that will join the thread automatically when the thread goes out of scope.

RAII Thread Wrapper


Output: We are in func1
We are in func2

Here we are creating an object of Wrapper i.e. w. In the constructor, we are passing the pointer to thread t1. The constructor assigns its pointer member variable std::thread *t with the pointer of thread t1. When the exception is thrown and func1() ends, the destructor of the Wrapper Class is invoked and it joins t1 automatically.

2. Scoped Pointers:

Scoped pointers are pointers that point to a heap-allocated memory and are deleted when the scope in which they are defined ends. Memory leak occurs when we forget to delete pointers that point to heap-allocated memory. This can exhaust our heap. So, it is very important to delete and free the heap memory we are not going to use.

The question is that if we want to delete the memory when the scope ends, why not use the stack memory? Obviously, we should always use stack memory whenever we can because it is faster to initialize and easier to manage. We only use heap memory when we need the memory to be shared between different scopes. So, what is the use case of scoped pointers?

When you need to allocate a large amount of memory, The stack might not be helpful because stack size is limited. If allocate more memory in the stack, stack overflow will occur. So to allocate large amounts of memory, we use heap allocation.

Scoped Pointer:


Output: Constructor: Creating int
Constructor: Creating string
Destroying string, Value: Hello
Destroying int, Value: 5

In this program, we are creating a class Scoped_ptr and passing a void pointer to it, so that we can support all types. Since we need to know the kind of pointer when we delete it, we also pass a parameter 'type' and cast our pointer to that type. When the scope ends, the heap-allocated object gets deleted automatically.

This implementation is just an example of how Scoped Pointers can be implemented. The real implementation of Scoped Pointer is based on templates. You can learn templates and implement Scoped pointers using them as an exercise.

Conclusion

In supported languages, RAII is a powerful tool to abstract resources without manually managing them. It prevents leaks and nasty bugs. Developers should use it to make their life easier.

Comment
Article Tags:
Article Tags: