C++
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Object-Oriented C++
-
Section 4: Memory Management
-
Section 5: Templates and Generic Programming
-
Section 6: The Standard Template Library
-
Section 7: Modern C++ Features
-
Section 8: Error Handling
-
Section 9: Multithreading
-
Section 10: Operator Overloading and Type Conversion
-
Section 11: Advanced Topics
-
Section 12: Tooling and Build Systems
-
Section 13: Design Patterns in C++
-
Section 14: Interfacing with C and Systems Programming
-
Section 15: Networking and IPC Basics
-
Section 16: Graphics and Game Programming Basics
-
Section 17: The Boost Libraries
-
Section 18: Data Structures and Algorithms in C++
-
Section 19: Practical Projects
-
Section 20: More Concurrency Patterns
-
Section 21: More OOP and Design Practice
-
Section 22: File I/O and Streams
-
Section 23: More Standard Library
-
Section 24: Practice Exercises
-
Section 25: Interview and Algorithm Practice
-
Section 26: Compiler and Language Internals
-
Section 27: GUI and Application Frameworks Overview
-
Section 28: Testing and Quality Practices
-
Section 29: Numerics Library
-
Section 30: Concepts Library (C++20)
-
Section 31: Ranges Library (C++20) In Depth
-
Section 32: More Utility Library
137: The Observer Pattern
I've seen this bug a hundred times in game engine code. You're building a system where one object needs to tell a bunch of other objects that something happened—like a player taking damage—and you decide to implement a simple notification list. It seems straightforward until the program suddenly crashes with a segmentation fault the moment you close a menu or destroy an enemy.
Take a look at this snippet. It's a classic "naive" observer implementation:
class HealthComponent {
std::vector<Observer*> observers;
public:
void addObserver(Observer* o) { observers.push_back(o); }
void takeDamage(int amount) {
// ... update health ...
for (auto o : observers) o->onNotify();
}
};
// Somewhere else in the code
{
UIHealthBar bar;
playerHealth->addObserver(&bar);
} // 'bar' goes out of scope and is destroyed here
playerHealth->takeDamage(10); // CRASH!
The "Zombie" Observer Crash
The problem here is that the HealthComponent (the Subject) has no idea that the UIHealthBar (the Observer) has been deleted. It's still holding a raw pointer to a memory address that no longer contains a valid object. When takeDamage is called, the loop tries to invoke onNotify() on a "zombie" object. This is the number one trap with the Observer pattern in C++: the Subject's lifetime often outlasts the Observer's lifetime.
Implementing a Safe Detach Mechanism
To fix this, we can't just hope the objects live forever. We need a formal way for an observer to say, "I'm leaving, stop talking to me." I usually handle this by adding a removeObserver method to the subject and calling it in the observer's destructor. It's a bit of manual bookkeeping, but it's explicit and fast.
class Subject {
std::vector<Observer*> observers;
public:
void addObserver(Observer* o) { observers.push_back(o); }
void removeObserver(Observer* o) {
observers.erase(std::remove(observers.begin(), observers.end(), o), observers.end());
}
void notify() {
for (auto o : observers) o->onNotify();
}
};
class UIHealthBar : public Observer {
Subject* subject;
public:
UIHealthBar(Subject* s) : subject(s) {
subject->addObserver(this);
}
~UIHealthBar() {
subject->removeObserver(this); // The critical fix
}
void onNotify() override { /* update the bar */ }
};
Now, when UIHealthBar is destroyed, it cleans up after itself. The Subject is left with a clean list of pointers, and the crash disappears. If you're working in a more modern C++ environment, you might be tempted to use std::shared_ptr everywhere, but be careful—if the Subject holds a shared_ptr to the Observer and the Observer holds one to the Subject, you've just created a circular dependency, and neither will ever be deleted. If you want "automatic" cleanup, use std::weak_ptr in your observer list instead.
Decoupling with an Interface
The real power of the Observer pattern isn't just the notification—it's the decoupling. The HealthComponent doesn't know that a UIHealthBar, a SoundManager, or an AchievementTracker exists. It only knows it has a list of things that implement the Observer interface.
I highly recommend keeping your Observer interface as lean as possible. Avoid passing too much data through the onNotify method. If the observer needs more details, let it query the subject back. This keeps your interface stable even as your game or application grows in complexity. If you find yourself adding onNotifyPlayerDeath(), onNotifyPlayerHeal(), and onNotifyPlayerLevelUp() to the interface, you're no longer using the Observer pattern; you're just writing a complex callback system. At that point, consider using a more robust Event Bus.
📋 Practical Task
Build a Real-Time Stock Market Alert System
Create a program that simulates a stock price tracker using the Observer pattern. Your implementation must include the following:
- A
Stockclass (the Subject) that holds a ticker symbol (e.g., "AAPL") and a current price. It should allow observers to attach and detach. - An
IStockObserverabstract base class with a pure virtualupdate(float price)method. - Two concrete observer classes:
PriceAlert: Triggers a message to the console only when the price crosses a specific threshold (e.g., "Alert! AAPL has hit $150!").PortfolioTracker: Maintains a running total of the "value" of the stock for a given number of shares.
- A
mainfunction that:- Creates a stock.
- Attaches both observers.
- Changes the price several times to trigger alerts.
- Deletes one of the observers mid-execution to prove your
detachlogic prevents memory crashes.
There are no comments for now.