Lesson 6 · 6 of 20
RAII and destructors · C++
Release resources without forgetting with RAII and destructors in C++: a connection that closes itself and a transaction that rolls back on errors.

The destructor is the method named after the class with a ~ in front, such as ~DbConnection(). It takes no parameters and C++ calls it automatically when the object stops existing: when its { } block ends, when the function returns or when the object that contains it is destroyed.
RAII (*Resource Acquisition Is Initialization*) builds on that guarantee: the constructor acquires a resource (a connection, a file, a lock) and the destructor releases it. Code that uses the class never has to remember to close anything: DbConnection db("bank.db"); opens it, and the closing happens by itself. That is how std::ofstream, std::vector and std::lock_guard work.
Transaction goes one step further: its destructor checks committed_ and, if nobody called commit(), rolls the changes back. In transfer(200) everything goes well: it commits and then the connection closes. Local objects are destroyed in reverse order of creation: first tx, then db.
In transfer(900) an exception is thrown before commit(). As the exception leaves the function, C++ destroys the local objects (this is called *stack unwinding*): the transaction rolls back, the connection closes and only then does the catch in main print the error. With RAII there is no need to repeat the clean-up on every way out.
The FLTK desktop apps that DMarlon Studio generates follow this same pattern for windows and resources. This example, like every one in the course, runs with the Console target by pressing F9.
Code · main.cpp
#include <iostream>#include <stdexcept>#include <string>// RAII: the constructor acquires a resource and the destructor releases it.class DbConnection { std::string name_;public: explicit DbConnection(std::string name) : name_(name) { std::cout << "open " << name_ << "\n"; } ~DbConnection() { std::cout << "close " << name_ << "\n"; } // destructor};// A transaction rolls back by itself unless commit() was called.class Transaction { bool committed_ = false;public: Transaction() { std::cout << " begin\n"; } void commit() { committed_ = true; std::cout << " commit\n"; } ~Transaction() { if (!committed_) std::cout << " rollback\n"; }};void transfer(int amount) { DbConnection db("bank.db"); Transaction tx; if (amount > 500) throw std::runtime_error("limit exceeded"); tx.commit();} // leaving the scope: tx is destroyed first, then db (reverse order)int main() { transfer(200); try { transfer(900); } // the exception leaves transfer(), but the destructors run catch (const std::exception& e) { std::cout << "Error: " << e.what() << "\n"; }}Output · c++ -std=c++17 main.cpp -o main && ./main
open bank.db begin commit close bank.db open bank.db begin rollback close bank.db Error: limit exceeded
Key points
- The destructor,
~Class(), is called automatically when the object goes away. - RAII: the constructor acquires the resource and the destructor releases it.
- Local objects are destroyed in reverse order of creation.
- Destructors still run when an exception leaves the function.
- A destructor should never throw.
Exercise
Add a FileLock class that prints lock when created and unlock when destroyed, and create one in transfer before db. Look at the order of the messages in both cases.