4 ms·
Could someone please explain the usefulness of RAII? As I understand it, RAII refers to the initialization semantics of certain languages, most famously C++.
by omginternets 4y ago
Could someone please explain the usefulness of RAII? As I understand it, RAII refers to the initialization semantics of certain languages, most famously C++. If I remember correctly, it effectively states that variable declarations amount to reserving memory resources and nothing else; memory is not zeroed, for instance.
My question is: what makes this so useful? Looking at wikipedia, the focus seems to be on lifetime guarantees, but I'm having trouble understanding how these are useful in practice. Contrasting this with a non-RAII language like Go might be helpful.
- 8organicbits 4y agoGo has defer, so you can write: f = os.Open(...) defer f.Close() // do stuff with f... This ensures you close the file at the end of the scope. If processing the file is complex, with lots of places where return is called, then there's lots of places where the Close call could actually need happen. Thankfully you just write it once with defer, and you can forget about it. In languages without defer you need something else. The RIIA approach is to create a file object where the constructor opens the file and the destructor closes the file. The compiler needs to track the file objects scope in order to call the destructor. This acts as a hook to run some code right at the end of the scope, wherever that actually occurs. Both call close right before returning, but the actual mechanism is different because the languages have different tools.
- xjay 4y ago"RAII" has some overlapping associations. Primary purpose: To guarantee the cleanup of resources during stack unwinding, after an exception is thrown, because it's tightly bound to the automatic storage duration mechanism (the static code generated to manage the stack at runtime). Secondary purposes: 1) To immediately initialize some resource handler where it is declared. 2) To automate the cleanup of some resource when execution leaves the scope in which it was declared, also hiding explicit calls to Close, Free, Unlock, Release, etc. To restate the source, RAII was the premise for exceptions. [1] [1] C++ in Constrained Environments - Bjarne Stroustrup - CppCon 2022 https://www.youtube.com/watch?v=2BuJjaGuInI&t=4000s https://www.youtube.com/watch?v=2BuJjaGuInI&t=4000s
- usefulcat 4y agoRAII just uses c++ constructors, destructors and lifetime rules to automatically do one thing when an object is constructed on the stack, and automatically do another thing when control leaves the scope in which the object exists. The second part (the destructor) is key, because it will be executed whether control leaves 'normally' or because an exception was thrown.
- cryptonector 4y agoRAII is awfully misnamed. It's not about initialization. It's about cleanup. The initialization aspect of RAII is incidental to its main purpose/use -- perhaps essential to it too, but not so essential that initialization becomes more the essence of RAII than cleanup. It's really a C++ specific form of Lisp's unwind-protect.
- gjulianm 4y ago> If I remember correctly, it effectively states that variable declarations amount to reserving memory resources and nothing else; memory is not zeroed, for instance. Not really. The idea is that any resource lifecycle management should be tied to the lifecycle of an object, therefore removing a whole class of problems. For example, when you open a file handle, the "open" operation should be tied to a class constructor, and the corresponding cleanup to the destructor. If you lock a mutex, that operation should be handled by a constructor, and the release by a destructor, so that you never forget to do the cleanup. > My question is: what makes this so useful? Looking at wikipedia, the focus seems to be on lifetime guarantees, but I'm having trouble understanding how these are useful in practice. I think Wikipedia should have an example of the things that RAII makes you avoid. Compare these two designs class Config { Config() { this->file = nullptr; } void load(const std::string& path) { this->file = open(path); } void close() { this->file->close(); } versus class Config { Config(const std::string& path) { this->file = open(path); } ~Config() { this->file->close(); } The first design makes it easy to forget calling close() before releasing the object, or maybe writing a destructor that does a double free if close() had already been called... The second design is clear, and also safe to use around exceptions. As for the comparison with another language... I don't know enough about Go, but RAII is present in a lot of object oriented languages. For example, in Python, opening a file creates a "file" object with the resource (the file handle) already allocated, and when that object refcount goes to zero, the underlying handle is released. However, because in modern languages all the "resource management" is usually done in standard libraries and memory allocation isn't done manually, programmers of those won't really hear about RAII, because all that is managed already in the standard libraries.