OOP Foundations
Object-oriented programming reorganizes how programs model complexity: instead of mapping everything to the solution space (computer operations), you create objects that represent concepts from the problem space itself, then tell them what to do. The result is code that reads like a description of the problem, not a list of machine instructions.
Progress of Abstraction
| Era | Languages | Model |
|---|---|---|
| Machine code | Assembly | Direct hardware operations |
| Procedural | C, Fortran, BASIC | Functions on data; solution space |
| OOP | C++, Java, Smalltalk | Objects in the problem space |
Procedural programs force you to build an intermediate representation in your head to map problem to code. OOP narrows this gap by letting you name and model problem-space entities directly.
Alan Kay’s 5 OOP Characteristics (Smalltalk origin)
- Everything is an object — each object stores data and can receive requests; even a number or a character is an object in principle
- A program is a bunch of objects sending messages — you request things by sending a message (calling a function)
- Each object has its own memory made up of other objects — new objects are composed of existing objects
- Every object has a type (class) — each object is an instance of a class
- All objects of a particular type can receive the same messages — substitutability: a Circle and a Square are both Shapes and both respond to
draw()
The Object Interface
An interface is the set of requests you can make of an object. It is defined by the object’s class. The UML class diagram represents a class as a box: type name on top, interface (member functions) listed below. An object’s interface is the only thing the outside world needs to know.
Hidden Implementation (Encapsulation)
The class creator hides implementation details from the client programmer using access specifiers:
| Specifier | Visibility |
|---|---|
public | Anyone |
private | Class creator only (no derived classes) |
protected | Class creator + derived classes |
This separation lets the class creator change internals without breaking client code, and prevents clients from depending on implementation details.
Composition (“Has-a”)
New classes are built from existing ones by embedding objects as member variables. A Car has-a Engine. This is often called aggregation when the relationship is looser.
- More flexible than inheritance — you can change the composed objects at runtime
- Preferred when the new type uses the capabilities of an existing type but isn’t fundamentally of that type
Inheritance (“Is-a” and “Is-like-a”)
A derived (child) class inherits the interface and often the implementation of a base (parent) class. The derived class is-a base class.
- Overriding: derived class replaces a base class function with its own version
- “Is-a”: derived class has exactly the same interface as base (pure substitution)
- “Is-like-a”: derived class extends the base with additional members (more complex to substitute)
Polymorphism
The ability to treat a derived-class object as its base type. When you call a function on a base-type pointer/reference, the correct derived-class version runs:
| Mechanism | When | How |
|---|---|---|
| Early binding (C default) | Compile time | Fixed function address; fast but inflexible |
| Late binding (OOP) | Runtime | Address looked up at call time; virtual keyword |
The virtual keyword in C++ tells the compiler to generate a lookup table (vtable) so the right function runs for the actual object type, not the pointer type. This enables extensibility: write code against the base type, add new derived types later without changing the code.
Upcasting: treating a derived object as its base type (the “upward” direction in an inheritance hierarchy). Safe and implicit in C++.
Creating and Destroying Objects
| Location | Allocated by | Lifetime |
|---|---|---|
| Stack | Compiler | Scoped — destroyed when block exits |
| Heap | Programmer (new / delete) | Controlled explicitly |
Stack objects are fast and automatically cleaned up. Heap objects are flexible (size and lifetime unknown at compile time) but require delete or memory leaks. C++ has no garbage collector — programmer responsibility.
Exception Handling
When an error occurs, the code at the error site throws an exception object. Execution jumps to an exception handler (catch block), bypassing all normal code in between. This guarantees that errors are either handled or propagated — unlike C error codes which are easily ignored.
graph TD A[Problem Space] --> B{OOP Mapping} B --> C[Objects = Problem Entities] B --> D[Messages = Requests] C --> E[Encapsulation: hidden implementation] C --> F[Composition: has-a] C --> G[Inheritance: is-a] G --> H[Polymorphism: virtual dispatch] H --> I[Extensibility: add types without changing code] C --> J[Lifetime: stack vs heap]
Key Points
- The 5 characteristics together define what makes something “OOP” — not just classes, but composition + inheritance + polymorphism
- Encapsulation protects: changes to implementation don’t break clients
- Composition is usually preferable to inheritance — simpler, more flexible
- Polymorphism via
virtualis what enables the open/closed principle: open for extension, closed for modification - No GC in C++:
newmust be paired withdelete; use RAII or smart pointers in modern C++ - Common design error: too little expert feedback early → bad tangents that are hard to unlearn; get a mentor