C++ Beginner · Lesson 13 of 22
RAII: deterministic cleanup, and why ROS 2 uses it everywhere
Acquire in the constructor, release in the destructor, and stay leak-free.
Estimated time · 60–90 min
Learning goals
What you will understand
- Explain raii: deterministic cleanup, and why ros 2 uses it everywhere in your own words
- Use it in a small robotics example
- Verify the result instead of assuming it worked
Understand the idea first
The mental model
RAII (Resource Acquisition Is Initialization) ties a resource to an object's lifetime: acquire it in the constructor and release it in the destructor. C++ guarantees the destructor runs when the object leaves scope, including on early return and exceptions, so cleanup is deterministic.
ROS 2 relies on this everywhere: a node, publisher, subscription and timer are objects whose lifetime controls the connection to the middleware, and rclcpp::init/shutdown, locks and file handles follow the same rule. Learn the pattern here and the ROS 2 API will feel natural.
Guided lesson
Work through these steps
- 1.
Read the raii: deterministic cleanup, and why ros 2 uses it everywhere example and identify what each line owns or changes.
- 2.
Create a small source file, compile with -std=c++17 and warnings enabled, and run it.
- 3.
Change one input, predict the result before running, then compare the output.
- 4.
Record one compiler or runtime error and explain the correction in your own words.
Worked example
Read it before you run it
class MotorSession {
// A type whose lifetime means 'the motor connection is open'.
public:
// Members below are public.
MotorSession() { std::cout << "connect\n"; }
// Constructor: acquire the resource the moment the object exists.
~MotorSession() { std::cout << "disconnect\n"; }
// Destructor: release it automatically when the object dies.
MotorSession(const MotorSession&) = delete;
// Forbid copying so two objects cannot both close the same connection.
MotorSession& operator=(const MotorSession&) = delete;
// Forbid copy assignment for the same reason.
};
// End of the class.
void run() {
// A function that uses the session.
MotorSession session;
// Prints connect.
if (!ready()) return;
// Early return: the destructor still runs and prints disconnect.
std::cout << "working\n";
// Normal work.
}
// session leaves scope here: disconnect is printed on every path.What the example is doing
- 1.Read declarations from right to left: identify each value's type, name, and initial value.
- 2.Follow the data through each expression before considering syntax shortcuts.
- 3.Compile with warnings enabled; a clean build is part of the result, not an optional polish step.
Watch it run · Values in memory
See what this code does
The terminal types this lesson's practice solution and runs it. The scene shows the kind of robot behaviour that code drives. It plays by itself and loops.
Demo for RAII: deterministic cleanup, and why ROS 2 uses it everywhere. Expected result: connect working disconnect
Practice code
Now type it yourself
Copy the starter into practice.cpp and replace every TODO with your own code. Build and run it: g++ -std=c++17 -Wall -Wextra -Wpedantic practice.cpp -o practice -pthread && ./practice
#include <iostream>
class MotorSession {
public:
// TODO: constructor prints "connect"; destructor prints "disconnect"
MotorSession(const MotorSession&) = delete;
MotorSession& operator=(const MotorSession&) = delete;
};
void run(bool ready) {
MotorSession session;
if (!ready) return; // the destructor still runs on this early return
std::cout << "working\n";
}
int main() {
run(true);
return 0;
}Expected output
connect
working
disconnectStuck? Show the full solution (try it yourself first)
#include <iostream>
class MotorSession {
public:
MotorSession() { std::cout << "connect\n"; }
~MotorSession() { std::cout << "disconnect\n"; }
MotorSession(const MotorSession&) = delete;
MotorSession& operator=(const MotorSession&) = delete;
};
void run(bool ready) {
MotorSession session;
if (!ready) return; // the destructor still runs on this early return
std::cout << "working\n";
}
int main() {
run(true);
return 0;
}Expected result
How you know it worked
- After adding the shown fragment to the guided exercise with its listed headers and surrounding program, the finished program builds with -Wall -Wextra -Wpedantic.
- Changing the stated input produces the result you predicted.
Practical exercise
Apply the lesson
Implement DeviceSession that logs connect on construction and disconnect on destruction. Call it from a function with an early return and one that throws, and prove cleanup happens both times.
Before you continue
- The program builds with -std=c++17 and no warnings
- You can explain the key rule without reading the example
- The normal and edge cases both have recorded results
If something goes wrong
Start at the first error. Check spelling, required #include lines, and whether the declaration appears before use.
Print the inputs and intermediate values, then check conversions, units, and collection order.