Inheritance and Polymorphism in C++
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
- Reproduce with minimal input.
- Read error messages completely.
- Binary-search the problem space by commenting out code.
- Compare against a known-good reference implementation.
- 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.