Designing software with C++: objects and polymorphism, safe resource handling, and reusable design patterns. Press play on any sketch to watch it happen.
1 Classes and polymorphism
A class bundles data with the functions that act on it, and hides the details. Polymorphism lets code work with a general type while each object does the right thing for itself.
Encapsulation: keep data private and expose a small public interface.
Inheritance: a derived class is a more specific version of its base.
Virtual function: the object's real type picks which version runs.
Composition: often better than inheritance: build from parts.
class Shape {
public:
virtual ~Shape() = default;
virtual double area() const = 0;
};
class Circle : public Shape {
double r;
public:
explicit Circle(double r) : r(r) {}
double area() const override { return 3.14 * r * r; }
};
UML class diagram
1/4 The calling code only knows it has a Shape.
Rule of thumb: Prefer "has a" over "is a". Deep inheritance trees are hard to change.
2 RAII and smart pointers
RAII ties a resource to an object's lifetime. The constructor acquires it and the destructor releases it, so cleanup happens automatically, even if an exception is thrown.
std::unique_ptr: one owner, freed automatically when it goes out of scope.
std::shared_ptr: shared ownership with a reference count.
Beyond memory: files, locks and sockets all follow the same pattern.
Rule of zero: let library types manage resources so you write no destructor at all.
void save() {
auto sensor = std::make_unique<Sensor>();
sensor->read(); // may throw
} // freed here, always
Lifetime diagram
1/4 A raw pointer: new Sensor runs on the way in.
Rule of thumb: If you are typing new or delete in modern C++, there is usually a better way.
3 Design principles and patterns
Design patterns are named solutions to problems that keep coming up. SOLID principles are guidelines for keeping classes easy to change. Both exist to reduce coupling.
Single responsibility: a class should have one reason to change.
Open/closed: add behaviour by extending, without editing working code.
Dependency inversion: depend on interfaces, not on concrete classes.
Observer: objects subscribe to be told when something changes.
Strategy and factory: swap an algorithm at runtime, and hide how objects are created.
Sequence of messages
1/4 The display and the logger subscribe to the sensor.
Rule of thumb: Use a pattern to solve a problem you have, not to show that you know it.