102. BufferTracker Destructor Logging

Create a class BufferTracker that simulates processing bytes in a communication buffer (UART, SPI, DMA, etc.).

Each time addByte() is called, an internal counter increases.
When the object is destroyed, the destructor must automatically print:

PROCESSED=<count>

This simulates firmware that logs how many bytes were processed during a communication transaction.

Class Requirements:

  • A private integer count, initialized to 0
  • Constructor initializes count = 0
  • void addByte(int b) increases count by 1
  • Destructor prints
    • PROCESSED=<count>
  • No static variables allowed

Program Behavior:

  1. Read integer n — number of bytes
  2. Create a BufferTracker object inside a scoped block { }
  3. Read n byte values (values are irrelevant)
  4. For each byte, call addByte()
  5. When the block ends, the destructor automatically prints the processed count

⚠️ Nothing should be printed inside the block — only the destructor prints output

 

Example 1

Input:

5
10 20 30 40 50

Output:

PROCESSED=5 

 

Example 2

Input:

0

Output:

PROCESSED=0 

 

Constraints:

  • Destructor must be the only place where output occurs
  • Count must exactly match the number of addByte() calls
  • Output must match exactly

 

 

 

 

Need Help? Refer to the Quick Guide below

A Destructor is a special member function that runs automatically when an object goes out of scope (e.g., function returns, block ends) or is explicitly deleted.

Its purpose is to release resources (memory, hardware locks, file handles) held by the object.

It has the same name as the class prefixed with a tilde (~), takes no arguments, and has no return type.

Syntax & Usage

1. Basic Destructor

Cleaning up when a "Scope" ends.

class LED {
    int pin;
public:
    LED(int p) : pin(p) { 
        gpio_write(pin, HIGH); // Turn ON when created
    }

    // Destructor
    ~LED() { 
        gpio_write(pin, LOW);  // Turn OFF when destroyed
    }
};

void blink() {
    LED status_led(13); // Constructor: LED turns ON
    delay(100);
} // End of scope: Destructor runs -> LED turns OFF automatically

2. RAII (The Scope Lock Pattern)

This is the most critical use case in firmware. It guarantees a resource is released even if you return early.

class MutexLock {
    Mutex& mtx;
public:
    MutexLock(Mutex& m) : mtx(m) { mtx.lock(); }   // Acquire
    ~MutexLock() { mtx.unlock(); }                 // Release
};

void critical_function() {
    MutexLock lock(system_mutex); // Mutex locked here

    if (error_detected) return;   // ✅ Safe! Destructor unlocks mutex automatically.
    
    process_data();
} // Destructor unlocks mutex here normally.

Lifecycle Visualization

EventAction
void func() {Start of scope.
Obj x;Constructor runs (Resource acquired).
...Code executes.
}End of scope. Destructor runs (Resource released).

Relevance in Embedded/Firmware

1. Resource Safety (RAII)

In C, if you forget to call Unlock() or Disable() before a return statement, your system deadlocks. Destructors automate this.

  • Interrupts: Disable in Constructor, Enable in Destructor.
  • Power: Wake up peripheral in Constructor, Sleep in Destructor.

2. Virtual Destructors (Polymorphism)

If you use Interfaces (Abstract Classes) in your HAL, you must make the destructor virtual.

If you don't, deleting a derived driver through a base pointer will not call the derived destructor, causing resource leaks (e.g., UART not turning off).

class IDriver {
public:
    virtual ~IDriver() {} // ✅ Essential for Interfaces
};

Common Pitfalls (Practical Tips)

PitfallDetails
❌ Virtual DestructorIf a class has virtual functions, it must have a virtual destructor. Otherwise, cleaning up via a Base pointer (Base* b = new Derived(); delete b;) will fail to clean up the Derived part.
❌ ExceptionsNever throw an exception inside a destructor. If a destructor crashes while another exception is being handled, the program terminates immediately.
❌ Manual CallNever call a destructor manually (obj.~Class()) unless you used Placement New. The compiler calls it for you. Calling it twice causes undefined behavior (double-free).
✅ Order of Destruction

Objects are destroyed in the reverse order of creation.

Lock a; Lock b;b is destroyed first, then a. This is crucial for nested locks.