09 Introduction to Object-Oriented Programming

A progressive guide to C++ object-oriented programming, covering classes, interfaces, encapsulation, composition, inheritance, and polymorphism with practical design criteria.

Classes, objects, and invariants

Object-oriented programming organizes software around objects: values that combine state with operations that work on that state. In C++, a defines a user-defined type, and an object is an instance of that type.

A can expose member functions while keeping representation details inside the type. For example, a bank-account type can provide an operation for depositing money and another for observing the balance, while preventing outside code from changing the balance directly.

A useful often protects an invariant: a condition that should remain true for every valid object. Validating deposits inside the is one way to ensure that the balance cannot be changed through an invalid operation.

Takeaway: Classes package state and behavior into meaningful types, and objects are the usable instances of those types.

Abstraction and interfaces

Abstraction means exposing the essential operations of an object while hiding details that users do not need to know. An interface communicates what an object does without requiring callers to depend on how it does it.

For example, a shape interface can require an operation that computes area without specifying how every shape stores its dimensions. A expresses such a required operation. A containing at least one is an , so clients create concrete shape objects rather than objects of the abstract interface itself.

A well-designed interface acts as a contract between parts of a program. It should be explicit, strongly typed, and limited to operations that callers genuinely need.

Takeaway: Abstraction separates the promises a type makes from the implementation details used to keep those promises.

and access control

combines data with the operations that manage it and controls access to the internal representation. It reduces the number of details other code must understand and helps a preserve its invariants.

C++ provides three main access levels:

  • public members are available through the general interface.

  • private members are accessible only to the 's members and permitted friends.

  • protected members are accessible to the and its derived classes, forming an interface intended for subclasses.

Members of a are private by default, whereas members of a struct are public by default. The choice should follow the type's design needs: a type that must coordinate access or protect a condition benefits from controlled access, while a simple aggregate may appropriately expose independently meaningful members.

Private implementation details can change without requiring every caller to change. For example, a document's public save operation can internally validate data and write it to storage while keeping those helper operations private.

Takeaway: controls dependencies and protects valid object states; it does not require every type to hide every member unconditionally.

and

builds a from objects of other classes as members. It expresses a “has-a” relationship. A car, for example, can contain an engine and delegate engine-specific work to that member.

keeps relationships explicit and limits dependencies. It is usually a better choice than when one type merely uses another type's functionality rather than being a genuine specialized form of it.

defines a new in terms of an existing base . With public , the derived represents a specialized form of the base and can be used where the base interface is expected. should therefore express a meaningful substitutable relationship, not merely provide convenient code reuse.

When a derived overrides a virtual member function, the override specifier asks the compiler to verify that the intended base function is actually being overridden. A polymorphic base commonly provides a when derived objects may be destroyed through base pointers or references.

Takeaway: Prefer for “has-a” relationships and use for a genuine, substitutable “is-a” relationship.

and design choices

allows one interface to represent multiple concrete forms. C++ supports through function overloading, templates, and -based runtime behavior.

With runtime , a function can accept a reference to an abstract shape interface and call its area operation. If the referenced object is a circle, selects the circle's implementation at runtime. The calling function depends only on the common interface, so additional concrete shape classes can be added without changing that caller when they satisfy the required operations.

References or pointers are important when an operation must preserve different derived object types. Passing a polymorphic object by value can copy the derived object into a base object and discard the derived part; this is known as object slicing.

A practical design often combines the main techniques: a public abstract interface defines operations, private members encapsulate implementation, assembles concrete objects, and virtual functions provide runtime . is one tool within this design, not the definition of object-oriented programming itself.

Takeaway: lets stable client code work with new concrete types through a shared interface, while references and pointers preserve the intended dynamic behavior.