ReviseAlgo Logo

Design Patterns in C++

Observer Pattern

Design reactive subscription models in C++ using std::weak_ptr to prevent reference cycles.

Interview: Managing object lifetimes, using weak_ptr to prevent circular reference leaks, and thread-safe notification loops.

Last Updated: June 13, 2026 9 min read

The Observer Pattern implements a subscription model where a subject notifies observers of state changes. In C++, you should store observer references using std::weak_ptr to avoid circular references.

Publish-Subscribe

Decouples subjects and observers. The subject manages registrations and triggers updates.

weak_ptr

Store observers as std::weak_ptr to break reference cycles and prevent memory leaks.

Cleanup

Remove expired observers dynamically from the subscription list during notification loops.

Preventing Reference Cycles

If a Subject holds a std::shared_ptr to an Observer, and the Observer holds a std::shared_ptr to the Subject, a circular reference occurs.

This prevents reference counts from reaching zero, causing both objects to leak. Storing observers as std::weak_ptr resolves this.

Code Walkthrough

A publish-subscribe pattern that cleans up expired observers during notification.

#include <iostream>
#include <vector>
#include <memory>

// Observer Interface class Observer { public: virtual void update(int price) = 0; virtual ~Observer() = default; };

// Concrete Observer class Investor : public Observer { private: std::string m_name;

public: Investor(std::string name) : m_name(name) {} void update(int price) override { std::cout << m_name << " notified of price update: $" << price << "\n"; } };

// Subject class StockTicker { private: std::vector<std::weak_ptr<Observer>> m_observers;

public: void attach(std::shared_ptr<Observer> obs) { m_observers.push_back(obs); }

void setPrice(int price) { // Notify observers, cleaning up expired ones dynamically for (auto it = m_observers.begin(); it != m_observers.end();) { if (auto sharedObs = it->lock()) { // Lock returns shared_ptr if object exists sharedObs->update(price); ++it; } else { it = m_observers.erase(it); // Remove expired observer from list } } } };

int main() { StockTicker ticker; auto inv1 = std::make_shared<Investor>("Alice");

ticker.attach(inv1); ticker.setPrice(150);

{ auto inv2 = std::make_shared<Investor>("Bob"); ticker.attach(inv2); ticker.setPrice(155); } // inv2 goes out of scope and is destroyed

ticker.setPrice(160); // Automatically cleans up inv2 from the observer list return 0; }

Interview-Relevant Information

Q: Why store observers as std::weak_ptr instead of std::shared_ptr?
Answer: Storing observers as std::shared_ptr keeps them alive as long as the subject is alive, which can prevent them from being garbage-collected. Using std::weak_ptr allows observers to be destroyed when their external owners go out of scope, avoiding memory leaks.

Q: How do you access the observer inside a weak_ptr vector?
Answer: You must call weak_ptr::lock() to convert the weak pointer to a temporary std::shared_ptr. If the observer has been destroyed, lock() returns nullptr, allowing you to identify and remove the expired observer.

Quick Checklist

Did you use std::weak_ptr to store observers? Do you clean up expired observers during notification? If yes, your Observer design is clean.

Use Cases

Implementing UI event listener registries where components attach and detach dynamically.

Developing stock tickers or telemetry systems that broadcast data updates to multiple clients.

Common Mistakes

Storing observers using std::shared_ptr, which creates circular reference loops and memory leaks.

Calling observer methods while holding a mutex, which can lead to deadlocks if observers perform locking operations.