cpp raii 1 - ghdrako/doc_snipets GitHub Wiki

Resource Acquisition Is Initialization

It is a resource management technique that acquires the resource during the construction of the RAII object and releases it during the destruction of that object. Unlike in languages such as Java and C# that use automatic garbage collection and whose resource release timing is not entirely clear to the user, C++ objects have a precisely defined storage duration and lifetime. Thus, we can rely on this characteristic and utilize RAII objects to manage our resources.

This technique is widely used no matter whether exceptions are available in the system or not. You can wrap your resources using RAII and be assured that they will be automatically released whenever you leave the scope where the RAII object lives.

The Standard Library has many examples of RAII objects, such as std::unique_ptr, std::lock_guard, and std::fstreams.

using namespace std;
class file_guard final {
public:
    file_guard(string_view file, mode_t mode) : // {5}
        fd{open(file.data(), mode)}
    {
        if (fd == -1) {
           throw system_error
           {make_error_code(errc{errno})};
        }
        cout << "File '" << file <<
        "' with file descriptor '" <<
        fd << "' is opened.\n";
    }
    explicit file_guard(const file_guard&) = delete; // {6}
    file_guard& operator=(const file_guard&) = delete;
    explicit file_guard(file_guard&& other) noexcept : //{7}
        fd{move(other.fd)} { other.fd = -1; }
    file_guard& operator=(file_guard&& other) noexcept
    {
        fd = move(other.fd);
        other.fd = -1;
        return *this;
    }
    int getFileDescriptor() const noexcept { // {8}
        return fd;
    }
    ~file_guard() noexcept { // {9}
        if (fd != -1) {
            close(fd);
            cout << "File with file descriptor '" << fd <<
              "' is closed.\n";
        }
    }
private:
    int fd;
};

The class gets in its constructor the file path and the mode in which the file should be opened; see marker {5}. In the initializer list of the constructor, the POSIX open() method is invoked. The result, which is the file descriptor ID, is assigned to the _fd member of the class. If open() fails, an exception is thrown away from the file_guard constructor. We should not care about closing the file in this case because we didn’t open it successfully. In the destructor of the class, we have the reversed operation; see marker {9}. If the file descriptor is different from -1, which means that the file has been successfully opened before that, we close it.

We have removed the copy constructor and the copy assignment operator and left the move constructor and move operator only, claiming that this RAII object is not copyable.

void Throw() {
    cout << "Ops, I need to throw ...\n";
    throw system_error{make_error_code
      (errc::bad_file_descriptor)};
}
int main() {
    const string_view myFileName{"/tmp/cpp-test-file"}; //
      {1}
    ofstream theFile(myFileName.data()); // {2}
    try {
        file_guard guard(myFileName, O_RDONLY); // {3}
        const auto fd = guard.getFileDescriptor();
        Throw(); // {4}
    } catch (const exception& e) {
        cout << e.what();
        return -1;
    }
    return 0;
}

If the file is opened (see marker {3}) and something goes wrong before it has been explicitly closed (see marker {4}), we will be sure that it will be automatically closed once the file_guard object goes out of scope.