C++ Programming Tutorial
Learn modern C++ step-by-step — from basics to advanced features like templates, STL, and smart pointers. Clear explanations with practical, runnable examples.
C++ Inheritance and Polymorphism
Inheritance lets you create new classes from existing ones, reusing and extending their code. Polymorphism allows calling derived class methods through base class pointers/references, enabling flexible and extensible designs.
Basic Inheritance
inheritance.cpp
#include <iostream>
#include <string>
// Base class
class Animal {
protected:
std::string name;
public:
Animal(std::string n) : name(std::move(n)) {}
void eat() const { std::cout << name << " is eating
"; }
std::string get_name() const { return name; }
};
// Derived class — inherits from Animal
class Dog : public Animal {
std::string breed;
public:
Dog(std::string n, std::string b)
: Animal(std::move(n)), breed(std::move(b)) {}
void bark() const { std::cout << name << " says Woof!
"; }
std::string get_breed() const { return breed; }
};
class Cat : public Animal {
public:
Cat(std::string n) : Animal(std::move(n)) {}
void purr() const { std::cout << name << " is purring
"; }
};
int main() {
Dog rex("Rex", "Labrador");
rex.eat(); // inherited from Animal
rex.bark(); // Dog-specific
Cat whiskers("Whiskers");
whiskers.eat();
whiskers.purr();
return 0;
}Virtual Functions and Polymorphism
polymorphism.cpp
#include <iostream>
#include <vector>
#include <memory>
class Shape {
public:
virtual double area() const = 0; // pure virtual — abstract class
virtual std::string type() const = 0;
virtual ~Shape() = default; // virtual destructor!
void describe() const {
std::cout << type() << ": area = " << area() << "
";
}
};
class Circle : public Shape {
double radius;
public:
Circle(double r) : radius(r) {}
double area() const override { return 3.14159 * radius * radius; }
std::string type() const override { return "Circle"; }
};
class Rectangle : public Shape {
double w, h;
public:
Rectangle(double w, double h) : w(w), h(h) {}
double area() const override { return w * h; }
std::string type() const override { return "Rectangle"; }
};
int main() {
// Polymorphism: different types through base class pointer
std::vector<std::unique_ptr<Shape>> shapes;
shapes.push_back(std::make_unique<Circle>(5.0));
shapes.push_back(std::make_unique<Rectangle>(3.0, 4.0));
shapes.push_back(std::make_unique<Circle>(2.0));
for (const auto& s : shapes) {
s->describe(); // calls correct derived method!
}
// Circle: area = 78.5398
// Rectangle: area = 12
// Circle: area = 12.5664
return 0;
}Key Concepts
| Concept | Keyword | Purpose |
|---|---|---|
| Virtual function | virtual | Enables runtime dispatch based on actual object type |
| Pure virtual | = 0 | Makes class abstract — cannot instantiate, must override |
| Override | override | Verifies method actually overrides a base virtual (catches typos) |
| Final | final | Prevents further overriding or inheritance |
| Virtual destructor | virtual ~Base() | Ensures correct cleanup when deleting through base pointer |
Access Control in Inheritance
access.cpp
class Base {
public: int pub; // accessible everywhere
protected: int prot; // accessible in Base and Derived
private: int priv; // accessible only in Base
};
// public inheritance (most common) — preserves access levels
class Derived : public Base {
void example() {
pub = 1; // OK — public in Base, public in Derived
prot = 2; // OK — protected in Base, protected in Derived
// priv = 3; // ERROR — private in Base, inaccessible
}
};
// Usage:
// Derived d;
// d.pub = 1; // OK
// d.prot = 2; // ERROR — protected from outside
// d.priv = 3; // ERROR — private to BaseBest Practices
- Always use
overrideon derived virtual methods — catches errors at compile time. - Always make base destructors virtual when using polymorphism through pointers.
- Prefer composition over inheritance for "has-a" relationships; use inheritance for "is-a" relationships.
- Use
std::unique_ptr<Base>for polymorphic ownership — safer than raw pointers. - Use pure virtual functions to define interfaces that derived classes must implement.
- Avoid deep inheritance hierarchies — prefer flat, interface-based designs.
Keep Practicing
Use the online compiler to run every example and experiment with modifications. The best way to learn C++ is by writing code — even small programs build strong foundations.