Documentation Index
Fetch the complete documentation index at: https://resources.devweekends.com/llms.txt
Use this file to discover all available pages before exploring further.
Object-Oriented Programming (OOP)
C++ is a multi-paradigm language, but its OOP features are powerful. OOP allows you to model real-world entities (like aPlayer, Car, or File) as objects that contain both data and behavior.
1. Classes & Structs
A Class is a blueprint for creating objects. It encapsulates data (attributes) and functions (methods) that operate on that data. In C++,class and struct are almost identical. The only difference is default visibility:
- class: Members are
privateby default. (Used for complex objects with invariants). - struct: Members are
publicby default. (Used for simple data containers).
Access Modifiers
- public: Accessible from anywhere. The “interface” of your class.
- private: Accessible only from within the class. Used for internal state.
- protected: Accessible from the class and its derived classes (children).
Class vs Struct — When to Use Which
| Criteria | Use class | Use struct |
|---|---|---|
| Has invariants (health cannot be negative, size must match capacity) | Yes — private data + public methods enforce rules | No |
| Has behavior (methods beyond simple getters) | Yes | Generally no |
| Is a simple data aggregate (Point, Color, Config) | Overkill | Yes — public members, no methods beyond constructors |
| Needs inheritance or polymorphism | Yes | Rarely (technically works, but signals wrong intent) |
| Used as a function parameter bundle or return type | Sometimes | Yes — clearly communicates “just data” |
struct, they expect plain data. When they see class, they expect encapsulated behavior.
2. Inheritance
Inheritance allows a class to derive properties and behavior from another. It promotes code reuse.3. Polymorphism
Polymorphism (“many shapes”) allows you to treat derived objects as base objects. This is the magic that lets you write flexible code. For example, you can have a list ofCharacter* pointers, some pointing to Wizards, some to Warriors. When you call attack(), the correct version is called for each object.
Virtual Destructors
Crucial: If a class has virtual functions, it must have a virtual destructor. Without it, deleting a derived object through a base pointer only runs the base destructor — the derived class’s destructor is silently skipped, leaking any resources it manages.4. Abstract Classes & Interfaces
A class is abstract if it has at least one pure virtual function (= 0). It cannot be instantiated. This is how C++ implements interfaces. It forces children to implement specific behavior.
5. The Rule of Five (and Why You Want Rule of Zero)
In modern C++, if you manage resources manually (like raw pointers), you need to define 5 special member functions to handle copying and moving correctly. If you define any one of them, you almost certainly need all five — the compiler’s auto-generated defaults will do the wrong thing for the others.- Destructor: Cleans up resources.
- Copy Constructor: Creates a new object from an existing one (Deep Copy).
- Copy Assignment Operator: Assigns an existing object to another (Deep Copy).
- Move Constructor (C++11): “Steals” resources from a temporary object (Performance).
- Move Assignment Operator (C++11): Assigns by stealing resources.
Rule of Zero — the real goal: If your class only uses RAII types (like
std::string, std::vector, std::unique_ptr), you don’t need to write ANY of these five functions. The compiler-generated defaults do the right thing automatically. This is not just a shortcut — it is the recommended practice. If you find yourself writing a destructor, ask: “Can I use a smart pointer or standard container instead?” The answer is almost always yes.Composition Over Inheritance
A quick but important design note: prefer composition (having objects as members) over inheritance (deriving from base classes). Inheritance creates tight coupling — changes to the base class ripple through every derived class. Composition is more flexible.Wizard is a Character) and when you need polymorphism. Use composition for everything else.
Inheritance vs Composition — Decision Framework
| Question to ask | If Yes | If No |
|---|---|---|
| Is the relationship truly “is-a”? (A Dog IS an Animal) | Inheritance is appropriate | Use composition |
| Do you need runtime polymorphism (virtual dispatch)? | Inheritance is the standard mechanism | Composition or templates work better |
| Does the derived class need to substitute for the base everywhere? (Liskov Substitution) | Inheritance is correct | Forced inheritance will cause bugs |
| Might the “base” be swapped out at runtime? (different engines, strategies) | Composition — inject the dependency | Inheritance locks you in at compile time |
| Are you combining capabilities from multiple sources? | Composition (C++ multiple inheritance is fragile) | Either approach can work |
6. OOP Gotchas That Cause Real Bugs
Gotcha 1: Object Slicing When you assign a derived object to a base variable by value, the derived part is “sliced off.” Only the base portion is copied. This is silent and almost never what you want.Character& c = w; preserves the full Wizard identity.
Gotcha 2: The Diamond Problem
When a class inherits from two classes that share a common base, the common base’s members appear twice in the derived object. This causes ambiguity.
virtual inheritance (class Dog : virtual public Animal) so only one copy of Animal exists. But virtual inheritance has overhead (extra pointer per object) and makes construction order complex. The real fix in most cases is to redesign so you do not need multiple inheritance from concrete classes.
Gotcha 3: Calling Virtual Functions in Constructors
Inside a constructor, virtual dispatch does NOT call the derived class’s override. The object is still being constructed, so the derived part does not exist yet. The base class version is always called.
init() method separately.
Exercises
- Slicing detector: Create a
Shapebase class witharea()and aCirclederived class. Write a function that takes aShapeby value and another that takes aShape&. Pass aCircleto both. Observe the difference. Add aprintstatement in eacharea()to prove which version is called. - Rule of Five practice: Implement a simple
Stringclass that wraps achar*buffer. Implement all five special member functions. Verify your move constructor works by returning aStringfrom a function and checking that no deep copy occurs (add print statements to each special member function). - Design challenge: You are building a notification system that can send alerts via Email, SMS, Slack, and Webhook. Should you use inheritance (a
Notifierbase class with derived classes) or composition (aNotificationServicethat holds aTransportstrategy)? Sketch both designs. Which is easier to extend when product asks for a PagerDuty integration next month? - Virtual destructor audit: Take any class hierarchy you have written and deliberately remove the
virtualkeyword from the base destructor. Delete a derived object through a base pointer. Compile with-Wnon-virtual-dtorand observe the warning. Run under AddressSanitizer and observe the runtime behavior.
Summary
- Classes: Encapsulate data and behavior.
- Inheritance: Enables code reuse (
publicinheritance is “is-a”). - Polymorphism: Allows dynamic behavior via Virtual Functions.
- Abstract Classes: Define interfaces/contracts.
- Rule of Five: Implement copy/move semantics only if managing raw resources. Otherwise, follow Rule of Zero.