C++ Programming · Lesson 4 of 9 · Updated ·
Inheritance and Virtual Functions in C++ — Polymorphism, Abstract Classes
Inheritance lets a class reuse another; virtual functions let one piece of code work with many related types (runtime polymorphism).
1. Basic inheritance
#include <iostream>
#include <string>
using namespace std;
class Employee {
protected: // visible to subclasses, not to outside code
string name;
double salary;
public:
Employee(string n, double s) : name(n), salary(s) {}
string describe() const { return name + " earns " + to_string((int)salary); }
};
class Manager : public Employee {
int teamSize;
public:
Manager(string n, double s, int t) : Employee(n, s), teamSize(t) {} // call the base constructor
string team() const { return name + " leads " + to_string(teamSize); }
};
int main() {
Manager m("Neha", 90000, 6);
cout << m.describe() << " | " << m.team() << "\n";
return 0;
}
Neha earns 90000 | Neha leads 6
Access modes
With class B : public A (the usual), A's public members stay public in B. protected / private inheritance makes them protected / private in B — rarely needed.
Construction order
Base constructor first, then derived; destructors in reverse order (derived first).
2. Virtual functions
#include <iostream>
#include <vector>
#include <memory>
using namespace std;
class Shape {
public:
virtual double area() const = 0; // pure virtual → Shape is abstract
virtual ~Shape() = default; // virtual destructor (important!)
};
class Circle : public Shape {
double r;
public:
explicit Circle(double r) : r(r) {}
double area() const override { return 3.14159 * r * r; }
};
class Square : public Shape {
double a;
public:
explicit Square(double a) : a(a) {}
double area() const override { return a * a; }
};
int main() {
vector<unique_ptr<Shape>> shapes;
shapes.push_back(make_unique<Circle>(1));
shapes.push_back(make_unique<Square>(3));
double total = 0;
for (const auto &s : shapes) total += s->area(); // the right area() runs
cout << total << "\n";
return 0;
}
12.1416
virtual: the call is decided by the actual object at runtime (through a hidden table of function pointers, the vtable).override: asks the compiler to check that you really override a virtual function (catches typos).= 0makes a function pure virtual; a class with one is abstract and can't be instantiated.
What happens without virtual?
#include <iostream>
using namespace std;
struct Animal { void speak() const { cout << "..."; } };
struct Dog : Animal { void speak() const { cout << "Woof"; } };
int main() {
Dog d;
Animal &a = d;
a.speak(); // not virtual → Animal::speak runs
cout << " ";
d.speak();
cout << "\n";
return 0;
}
... Woof
3. Why destructors must be virtual
If you delete a derived object through a base pointer and the base destructor is not virtual, only the base part is destroyed — a resource leak (formally undefined behaviour). Rule: a class with any virtual function should have a virtual destructor.
4. Multiple inheritance (briefly)
C++ allows class C : public A, public B. The diamond problem (A and B share a base) is solved with virtual inheritance (class A : virtual public Base). Use multiple inheritance sparingly — mostly for "interfaces" (classes with only pure virtual functions).
Common mistakes
- Forgetting
virtualin the base class → the wrong function runs. - Missing virtual destructor in a polymorphic base class.
- Object slicing: assigning a
Dogto anAnimalby value cuts off the Dog part; use references or pointers.
Quick recap
class Derived : public Base; call the base constructor in the initializer list.virtual+override= runtime polymorphism via the vtable.- Pure virtual (
= 0) → abstract class. - Polymorphic base classes need virtual destructors; avoid slicing.