पाठ 12 / 25

Inheritance, Virtual Functions and Polymorphism

Use virtual functions, override, abstract classes and virtual destructors correctly.

Run-time polymorphism in C++

A class can inherit from a base class with class Circle : public Shape. For run-time polymorphism, base-class member functions must be declared virtual; derived classes override them, and calls through a base reference or pointer dispatch to the derived version, usually via a virtual table. Mark overrides with override so the compiler checks the signature, and use final to forbid further overriding or inheritance. A pure virtual function (virtual double area() const = 0;) makes the class abstract: it cannot be instantiated, only derived from. Any base class meant to be deleted through a base pointer needs a virtual destructor; otherwise deleting a derived object through a base pointer is undefined behaviour and typically leaks the derived part. Beware object slicing: copying a derived object into a base value (Shape s = circle;) slices off the derived part, so polymorphism works only through references and pointers. C++ also supports multiple inheritance; use it mainly for interface-like abstract classes.

Abstract base class with virtual functions

Polymorphism through references and smart pointers.

#include <memory>
#include <numbers>
#include <print>
#include <vector>

class Shape {
public:
    virtual ~Shape() = default;                 // virtual destructor
    virtual double area() const = 0;            // pure virtual: Shape is abstract
    virtual const char* name() const { return "shape"; }
};

class Circle final : public Shape {
public:
    explicit Circle(double r) : r_(r) {}
    double area() const override { return std::numbers::pi * r_ * r_; }
    const char* name() const override { return "circle"; }
private:
    double r_;
};

class Square final : public Shape {
public:
    explicit Square(double s) : s_(s) {}
    double area() const override { return s_ * s_; }
    const char* name() const override { return "square"; }
private:
    double s_;
};

int main() {
    std::vector<std::unique_ptr<Shape>> shapes;
    shapes.push_back(std::make_unique<Circle>(1.0));
    shapes.push_back(std::make_unique<Square>(2.0));
    for (const auto& s : shapes) {
        std::print("{} has area {:.2f}\n", s->name(), s->area());   // dynamic dispatch
    }
}

Forgetting virtual on the destructor

std::unique_ptr<Shape> p = std::make_unique<Circle>(1.0); destroys the Circle through a Shape pointer. Without a virtual destructor in Shape, only Shape's destructor runs, which is undefined behaviour. Base classes with virtual functions should almost always have a virtual destructor.

त्वरित जाँच: What is object slicing?

  • Copying a derived object into a base-class value, losing the derived part
  • Splitting a class into two files
  • Calling a virtual function
  • Deleting an object twice
Answer

Copying a derived object into a base-class value, losing the derived part — Base-class values hold only the base part; use references or pointers for polymorphism.