Reading — step 1 of 7
Learn
Inheritance lets one class extend another, sharing implementation and creating an is-a relationship. Combined with virtual functions, it powers runtime polymorphism — the foundation of OOP in C++.
Basic inheritance
class Animal {
protected:
std::string name;
public:
Animal(std::string n) : name{std::move(n)} {}
void describe() const {
std::cout << "I am " << name << "\n";
}
};
class Dog : public Animal {
public:
Dog(std::string n) : Animal{std::move(n)} {}
void bark() const { std::cout << "Woof!\n"; }
};
Dog d{"Rex"};
d.describe(); // I am Rex (inherited from Animal)
d.bark(); // Woof!
class Dog : public Animal — Dog inherits from Animal publicly. Always use public inheritance for is-a relationships. Private/protected inheritance is rare and means "implemented in terms of" — usually composition is better.
Calling the base constructor
Use the member initializer list:
Dog(std::string n) : Animal{std::move(n)} {}
Must come BEFORE any of Dog's own member initializers.
Virtual functions — runtime polymorphism
Without virtual, calling a method through a base pointer/reference uses the base class's version (static dispatch):
class Shape {
public:
double area() const { return 0; } // NOT virtual
};
class Circle : public Shape {
double r;
public:
Circle(double r_) : r{r_} {}
double area() const { return 3.14159 * r * r; }
};
Shape* s = new Circle(5);
std::cout << s->area(); // 0 — not what you wanted!
Mark the base method virtual for dynamic dispatch:
class Shape {
public:
virtual double area() const { return 0; }
virtual ~Shape() = default; // ALWAYS virtual destructor in polymorphic bases
};
class Circle : public Shape {
double r;
public:
Circle(double r_) : r{r_} {}
double area() const override { return 3.14159 * r * r; }
};
Shape* s = new Circle(5);
std::cout << s->area(); // 78.54 — polymorphic dispatch
delete s;
override — say what you mean
Mark overriding methods with override. The compiler will error if the base method doesn't actually exist as virtual:
double area() const override { return 3.14159 * r * r; }
Without override, a typo in the signature silently creates a NEW method (hiding instead of overriding). Always use override — it's a free correctness check.
Pure virtual and abstract classes
virtual ... = 0 declares a method WITHOUT an implementation:
class Shape {
public:
virtual double area() const = 0; // pure virtual
virtual ~Shape() = default;
};
A class with at least one pure virtual is abstract — can't be instantiated directly. Subclasses must implement the pure virtuals or remain abstract themselves. This is C++'s equivalent of an interface.
Virtual destructors — CRITICAL
Shape* s = new Circle(5);
delete s; // calls Shape's destructor only — Circle's destructor is SKIPPED!
If ~Shape isn't virtual, deleting a derived object through a base pointer leaks resources. Rule: if a class has any virtual function, give it a virtual destructor.
Common mistakes
- Forgetting
virtual— gets you static dispatch, which is the wrong default for polymorphic types. - Forgetting
override— typos silently create new methods. Always annotate. - No virtual destructor in a polymorphic base — leaks when deleting through a base pointer.
- Slicing:
Animal a = dog;copies only the Animal portion of dog. UseAnimal&orAnimal*for polymorphism. - Multiple inheritance with shared base classes — diamond problem. Use
virtualinheritance carefully or restructure.
Discussion
Ask a question, share an insight, or help someone who’s stuck.
Sign in to post a comment or reply.
Loading…