Inheritance enables code reuse and runtime polymorphism via virtual functions. Prefer composition when inheritance isn’t a true ‘is-a’ relationship.

Basic Inheritance

  class Animal {
public:
    virtual void speak() const { std::cout << "...\n"; }
    virtual ~Animal() = default;
};

class Dog : public Animal {
public:
    void speak() const override { std::cout << "Woof!\n"; }
};
  

Polymorphism

  void greet(const Animal& a) { a.speak(); }

Dog d;
greet(d);  // Woof!

Animal* p = new Dog();
p->speak();
delete p;
  

Abstract Classes

  class Shape {
public:
    virtual double area() const = 0;  // pure virtual
    virtual ~Shape() = default;
};

class Circle : public Shape {
    double r_;
public:
    Circle(double r) : r_(r) {}
    double area() const override { return 3.14159 * r_ * r_; }
};
  

final and override

  class Base {
public:
    virtual void f() {}
};
class Derived final : public Base {
public:
    void f() override {}  // compiler checks signature
};
  

Composition Alternative

  class Engine { void start(); };
class Car {
    Engine engine_;  // has-a, not is-a
public:
    void start() { engine_.start(); }
};
  

Common Pitfalls

  • Ignoring compiler or linter warnings until they become production bugs.
  • Skipping error handling on I/O, allocation, and network operations.
  • Using outdated patterns when modern idioms exist in your language version.
  • Testing only the happy path without edge cases and failure modes.

Best Practices

  • Write tests alongside implementation, not after.
  • Prefer explicit, readable code over clever one-liners.
  • Use the standard library before reaching for third-party dependencies.
  • Profile before optimizing; measure after.
  • Document public APIs and non-obvious invariants.

Memory and Performance Notes

Virtual calls have vtable indirection cost. Avoid in hot inner loops when type is known.

Exercise

Create a hierarchy: Shape → Circle, Rectangle. Store in vector<unique_ptr> and print areas.

Hint: Use override keyword — catches typos in virtual function signatures.

Real-World Application

Production codebases combine these fundamentals with logging, metrics, and error recovery. Study mature open-source projects in this language for idiomatic patterns.

Summary

Master this topic through hands-on practice before advancing to the next section in the learning path.

Debugging Checklist

  1. Reproduce with minimal input.
  2. Read error messages completely.
  3. Binary-search the problem space by commenting out code.
  4. Compare against a known-good reference implementation.
  5. Write a regression test once fixed.

Quick Reference

Review the code examples on this page and type them manually — muscle memory accelerates learning.

Further Reading

C++ Core Guidelines, cppreference.com, and Effective Modern C++ by Scott Meyers.

Real-World Context

These patterns appear in Chromium, Unreal Engine, PostgreSQL, and countless production systems.

Additional Examples

Consider how this topic applies in a larger project:

  // Break the problem into smaller functions
// Test each function independently
// Integrate incrementally
  

Working through variations of the examples above builds deeper understanding than reading alone.