CodeTheRobot

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. 1.

    Read the raii: deterministic cleanup, and why ros 2 uses it everywhere example and identify what each line owns or changes.

  2. 2.

    Create a small source file, compile with -std=c++17 and warnings enabled, and run it.

  3. 3.

    Change one input, predict the result before running, then compare the output.

  4. 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. 1.Read declarations from right to left: identify each value's type, name, and initial value.
  2. 2.Follow the data through each expression before considering syntax shortcuts.
  3. 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.

motor speed030x1070x1410x1890x1c40x2060x2420x2880x2c*p += 10VALUES IN MEMORY

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
disconnect
Stuck? 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

The compiler reports that a name or type is unknown.

Start at the first error. Check spelling, required #include lines, and whether the declaration appears before use.

The program runs but the value is wrong.

Print the inputs and intermediate values, then check conversions, units, and collection order.