What is meant by Resource Acquisition is Initialization (RAII)?
Asked Answered
D

10

480

What is meant by Resource Acquisition is Initialization (RAII)?

Diarthrosis answered 23/2, 2010 at 20:35 Comment(3)
en.wikipedia.org/wiki/Resource_Acquisition_Is_InitializationFaggoting
This is what drives it home for me. stroustrup.com/bs_faq2.html#finallyKong
Microsoft reference with 3 sentences and 2 examples yet very clear! msdn.microsoft.com/en-us/library/hh438480.aspxTriptolemus
L
569

It's a really terrible name for an incredibly powerful concept, and perhaps one of the number 1 things that C++ developers miss when they switch to other languages. There has been a bit of a movement to try to rename this concept as Scope-Bound Resource Management, though it doesn't seem to have caught on just yet.

When we say 'Resource' we don't just mean memory - it could be file handles, network sockets, database handles, GDI objects... In short, things that we have a finite supply of and so we need to be able to control their usage. The 'Scope-bound' aspect means that the lifetime of the object is bound to the scope of a variable, so when the variable goes out of scope then the destructor will release the resource. A very useful property of this is that it makes for greater exception-safety. For instance, compare this:

RawResourceHandle* handle=createNewResource();
handle->performInvalidOperation();  // Oops, throws exception
...
deleteResource(handle); // oh dear, never gets called so the resource leaks

With the RAII one

class ManagedResourceHandle {
public:
   ManagedResourceHandle(RawResourceHandle* rawHandle_) : rawHandle(rawHandle_) {};
   ~ManagedResourceHandle() {delete rawHandle; }
   ... // omitted operator*, etc
private:
   RawResourceHandle* rawHandle;
};

ManagedResourceHandle handle(createNewResource());
handle->performInvalidOperation();

In this latter case, when the exception is thrown and the stack is unwound, the local variables are destroyed which ensures that our resource is cleaned up and doesn't leak.

Lawley answered 5/8, 2013 at 9:27 Comment(5)
@the_mandrill: I tried ideone.com/1Jjzuc this program. But there is no destructor call. The tomdalling.com/blog/software-design/… says that C++ guarantees that the destructor of objects on the stack will be called, even if an exception is thrown. So, why destructor didn't execute here? Is my resource leaked or will it be never freed or released?Thereon
An exception is thrown, but you aren't catching it, so the application terminates. If you wrap with a try { } catch () {} then it works as expected: ideone.com/xm2GR9Lawley
Not quite sure if Scope-Bound is the best name choice here since storage class specifiers together with the scope determine the storage duration of an entity. Narrowing it done to scope-bound is maybe a useful simplification, however it isn't 100% preciseConciliatory
But how do you explain RA is initialization in the original name? What I understand by RAII is it is the responsibility of each object to take care of its deletion once out of the scope. To me it doesn't match with is initialization because everything is related to the destructor. I am still confused by that idiom name.Querist
"... number 1 things that C++ developers miss ..." Isn't this similar to try-with-resources in Java? It seems to solve the same problem, and I see no advantage or disadvantage of RAII compared to Java's solution.Outflank
W
163

This is a programming idiom which briefly means that you

  • encapsulate a resource into a class (whose constructor usually - but not necessarily** - acquires the resource, and its destructor always releases it)
  • use the resource via a local instance of the class*
  • the resource is automatically freed when the object gets out of scope

This guarantees that whatever happens while the resource is in use, it will eventually get freed (whether due to normal return, destruction of the containing object, or an exception thrown).

It is a widely used good practice in C++, because apart from being a safe way to deal with resources, it also makes your code much cleaner as you don't need to mix error handling code with the main functionality.

* Update: "local" may mean a local variable, or a nonstatic member variable of a class. In the latter case the member variable is initialized and destroyed with its owner object.

** Update2: as @sbi pointed out, the resource - although often is allocated inside the constructor - may also be allocated outside and passed in as a parameter.

Woodwork answered 23/2, 2010 at 20:39 Comment(5)
AFAIK, the acronym doesn't imply the object has to be on a local (stack) variable. It could be a member variable of another object, so when the 'holding' object is destroyed, the member object is destroyed too, and the resource is released. In fact, i think the acronym specifically means only that there's no open()/close() methods to initialize and release the resource, just the constructor and destructor, so the 'holding' of the resource is just the lifetime of the object, no matter if that lifetime is handled by the context (stack) or explicitly (dynamic alloc)Armpit
Actually nothing says the resource must be acquired in the constructor. File streams, strings an other containers do that, but the resource might just as well be passed to the constructor, as is usually the case with smart pointers. Since yours is the most-upvoted answer, you might want to fix this.Skateboard
It is not an acronym, it is an abbreviation. IIRC most people pronounce it "ar ey ay ay" so it doesn't really qualify for an acronym like say DARPA, which is pronounced DARPA instead of spelled. Also, I'd say RAII is a paradigm rather than a mere idiom.Krieger
@Peter Torok: I tried ideone.com/1Jjzuc this program. But there is no destructor call. The tomdalling.com/blog/software-design/… says that C++ guarantees that the destructor of objects on the stack will be called, even if an exception is thrown. So, why destructor didn't execute here? Is my resource leaked or will it be never freed or released?Thereon
In that example, the exception isn't caught so the program terminates instantly. If you catch the exception then the destructor is called as the stack is unwound.Lawley
S
62

"RAII" stands for "Resource Acquisition is Initialization" and is actually quite a misnomer, since it isn't resource acquisition (and the initialization of an object) it is concerned with, but releasing the resource (by means of destruction of an object).
But RAII is the name we got and it sticks.

At its very heart, the idiom features encapsulating resources (chunks of memory, open files, unlocked mutexes, you-name-it) in local, automatic objects, and having the destructor of that object releasing the resource when the object is destroyed at the end of the scope it belongs to:

{
  raii obj(acquire_resource());
  // ...
} // obj's dtor will call release_resource()

Of course, objects aren't always local, automatic objects. They could be members of a class, too:

class something {
private:
  raii obj_;  // will live and die with instances of the class
  // ... 
};

If such objects manage memory, they are often called "smart pointers".

There are many variations of this. For example, in the first code snippets the question arises what would happen if someone wanted to copy obj. The easiest way out would be to simply disallow copying. std::unique_ptr<>, a smart pointer to be part of the standard library as featured by the next C++ standard, does this.
Another such smart pointer, std::shared_ptr features "shared ownership" of the resource (a dynamically allocated object) it holds. That is, it can freely be copied and all copies refer to the same object. The smart pointer keeps track of how many copies refer to the same object and will delete it when the last one is being destroyed.
A third variant is featured by std::auto_ptr which implements a kind of move-semantics: An object is owned by only one pointer, and attempting to copy an object will result (through syntax hackery) in transferring ownership of the object to the target of the copy operation.

Skateboard answered 21/11, 2010 at 21:22 Comment(2)
std::auto_ptr is obsolete version of std::unique_ptr. std::auto_ptr kind of simulated move semantics as much as it was possible in C++98, std::unique_ptr uses the new move semantics of C++11. New class was created because the move semantics of C++11 is more explicit (requires std::move except from temporary) while it was defaulted for any copy from non-const in std::auto_ptr.Seaweed
@JiahaoCai: Once, many years ago (on Usenet), Stroustrup himself said so.Skateboard
B
41

An object's lifetime is determined by its scope. However, sometimes we need, or it is useful, to create an object that lives independently of the scope where it was created. In C++, the operator new is used to create such an object. And to destroy the object, the operator delete can be used. Objects created by the operator new are dynamically allocated, i.e. allocated in dynamic memory (also called heap or free store). So, an object that was created by new will continue to exist until it's explicitly destroyed using delete.

Some mistakes that can occur when using new and delete are:

  • Leaked object (or memory): using new to allocate an object and forget to delete the object.
  • Premature delete (or dangling reference): holding another pointer to an object, delete the object, and then use the other pointer.
  • Double delete: trying to delete an object twice.

Generally, scoped variables are preferred. However, RAII can be used as an alternative to new and delete to make an object live independently of its scope. Such a technique consists of taking the pointer to the object that was allocated on the heap and placing it in a handle/manager object. The latter has a destructor that will take care of destroying the object. This will guarantee that the object is available to any function that wants access to it, and that the object is destroyed when the lifetime of the handle object ends, without the need for explicit cleanup.

Examples from the C++ standard library that use RAII are std::string and std::vector.

Consider this piece of code:

void fn(const std::string& str)
{
    std::vector<char> vec;
    for (auto c : str)
        vec.push_back(c);
    // do something
}

when you create a vector and you push elements to it, you don't care about allocating and deallocating such elements. The vector uses new to allocate space for its elements on the heap, and delete to free that space. You as a user of vector you don't care about the implementation details and will trust vector not to leak. In this case, the vector is the handle object of its elements.

Other examples from the standard library that use RAII are std::shared_ptr, std::unique_ptr, and std::lock_guard.

Another name for this technique is SBRM, short for Scope-Bound Resource Management.

Bunnie answered 12/11, 2018 at 6:14 Comment(5)
"SBRM" makes a lot more sense to me. I came to this question because I thought I understood RAII but the name was throwing me off, hearing it described instead as "Scope-Bound Resource Management" made me instantly realise that I did indeed understand the concept.Synchroscope
I am not sure why this was not mark this as the answer for the question. It is a very thorough and well-written answer, thanks @BunnieLarousse
I find "RAII can be used as an alternative to new and delete to make an object live independently of its scope." statement a bit odd. The standard containers and pointers you examplify such as std::vector and std::unique_ptr are already using "new" and "delete" under the hood. For instance, the destructor of the managing/handler objects contains delete statements to free the allocated subobject when they go out of scope. Does this mean that these standard classes are alternatives to the objects being constructed and destructed using "new" and "delete"?Once
@Once I was highlighting the shift from explicit memory management by the programmer to implicit, automated memory management by RAII classes. In manual memory management, the programmer is responsible for every allocation (new) and deallocation (delete) call. By using RAII classes like std::vector, std::unique_ptr, std::shared_ptr, etc., the responsibility of memory management is shifted from the programmer to these classes. I hope my point is clear now.Bunnie
@Bunnie Yes, fair enough.Once
M
17

The book C++ Programming with Design Patterns Revealed describes RAII as:

  1. Acquiring all resources
  2. Using resources
  3. Releasing resources

Where

  • Resources are implemented as classes, and all pointers have class wrappers around them (making them smart pointers).

  • Resources are acquired by invoking their constructors and released implicitly (in reverse order of acquiring) by invoking their destructors.

Miramontes answered 5/8, 2013 at 8:48 Comment(1)
@Brandin I've edited my post so that readers will focus on the content that matters, rather than debating the grey area of copyright law of what constitutes fair use.Miramontes
M
8

There are three parts to an RAII class:

  1. The resource is relinquished in the destructor
  2. Instances of the class are stack allocated
  3. The resource is acquired in the constructor. This part is optional, but common.

RAII stands for "Resource Acquisition is initialization." The "resource acquisition" part of RAII is where you begin something that must be ended later, such as:

  1. Opening a file
  2. Allocating some memory
  3. Acquiring a lock

The "is initialization" part means that the acquisition happens inside the constructor of a class.

https://www.tomdalling.com/blog/software-design/resource-acquisition-is-initialisation-raii-explained/

Mahlstick answered 11/7, 2018 at 11:24 Comment(0)
A
5

Manual memory management is a nightmare that programmers have been inventing ways to avoid since the invention of the compiler. Programming languages with garbage collectors make life easier, but at the cost of performance. In this article - Eliminating the Garbage Collector: The RAII Way, Toptal engineer Peter Goodspeed-Niklaus gives us a peek into the history of garbage collectors and explains how notions of ownership and borrowing can help eliminate garbage collectors without compromising their safety guarantees.

Argue answered 28/1, 2016 at 17:33 Comment(0)
R
3

Many argue that RAII is a misnomer, but actually it is a right name for this idiom, just it is not explained well.

Wikipedia explained behavior in detail: Resource acquisition is initialization (RAII) is a programming idiom used in several object-oriented, statically-typed programming languages to describe a particular language behavior. In RAII, holding a resource is a class invariant, and is tied to object lifetime: resource allocation (or acquisition) is done during object creation (specifically initialization), by the constructor, while resource deallocation (release) is done during object destruction (specifically finalization), by the destructor. In other words, resource acquisition must succeed for initialization to succeed. Thus the resource is guaranteed to be held between when initialization finishes and finalization starts (holding the resources is a class invariant), and to be held only when the object is alive. Thus if there are no object leaks, there are no resource leaks.

And now for the name, it simply means the action of "resource acquisition" is an action of initialization and should be part of the initialization/constructor of the resource class object. In other word, using this idiom, working with a resource means making a resource class to hold the resource and initialize resource at time of constructing the class object. Implicitly, it suggests the deallocation of the resource should happen symmetrically in the resource class destructor.

How is this useful? You can of course choose not to use this idiom, but if you wonder what you would get with this idiom, consider

RAII It is quite common for even larger C++ projects to not contain a single call to new or delete (or malloc/free) outside of a constructor/destructor pair. Or at all, in fact.

And you can avoid the

exit: free_resouce() // clean resource before exit function

or use a RAII lock so you never forget to unlock.

Repair answered 1/12, 2020 at 20:10 Comment(0)
O
2

RAII concept is just a C stack variable idea. the simplest way to explain.

Ovotestis answered 1/10, 2020 at 14:8 Comment(0)
E
2

I've come back to this question several times and read it, and I think the highest voted answer is a little bit misleading.

They key for RAII is:

"It's (mostly) not about catching exceptions, it's mostly about managing ownership of resources."

The highest voted answer overstates exception-safe, which made me confused.

The fact is that:

  1. You still need to write try catch to handle exceptions (check the 2 code example below), except that you don't need to worry about releasing resources for those classes using RAII in your catch block. Otherwise, you need to look up each non-RAII class's API to find which function to call so as to release acquired resources in catch block. RAII simply save these work.

  2. Similar as above, when coding with RAII, you simply write less code, no need to call releasing resouce functions. All the clean-ups are done in the destructor.

Also, check these 2 code examples I found useful in comments above.

https://ideone.com/1Jjzuchttps://ideone.com/xm2GR9

P.S. One can compare with the python with .. as statement, you need to catch the exception which could take place inside the with block too.

Exteroceptor answered 11/9, 2021 at 5:13 Comment(1)
The examples are somewhat nebulous since they are not valid C++. The lines int *p = new int[-1]; do not compile.Hamrnand

© 2022 - 2024 — McMap. All rights reserved.