Inheritance and Polymorphism¶
Runtime polymorphism via virtual functions and inheritance hierarchies. Base class pointers/references invoke derived behavior through vtable dispatch.
Key Facts¶
- Three access specifiers:
public,protected,private publicinheritance = IS-A relationship (most common)protectedinheritance = implemented-in-terms-of (rare)privateinheritance = implemented-in-terms-of (prefer composition)- Constructor order: base -> members -> derived body. Destructor: reverse
virtualfunction: dispatched via vtable at runtime. Overhead: one pointer per object + indirect calloverridekeyword (C++11): compiler checks that function actually overrides base virtualfinalkeyword: prevents further overriding or derivation- Pure virtual
= 0: makes class abstract, cannot instantiate - Virtual destructor required in any class used as polymorphic base
- Multiple inheritance: supported but use with caution; virtual inheritance solves diamond problem
dynamic_cast<Derived*>(base_ptr): safe downcast, returns nullptr on failure (RTTI)
Patterns¶
Basic Inheritance¶
class Shape {
protected:
double x_, y_; // position
public:
Shape(double x, double y) : x_(x), y_(y) {}
virtual ~Shape() = default; // ALWAYS virtual dtor for base
virtual double area() const = 0; // pure virtual
virtual double perimeter() const = 0; // pure virtual
virtual void draw() const { /* default impl */ }
double x() const { return x_; }
double y() const { return y_; }
};
class Circle : public Shape {
double radius_;
public:
Circle(double x, double y, double r) : Shape(x, y), radius_(r) {}
double area() const override { return 3.14159 * radius_ * radius_; }
double perimeter() const override { return 2 * 3.14159 * radius_; }
};
class Rectangle : public Shape {
double w_, h_;
public:
Rectangle(double x, double y, double w, double h)
: Shape(x, y), w_(w), h_(h) {}
double area() const override { return w_ * h_; }
double perimeter() const override { return 2 * (w_ + h_); }
};
Polymorphic Usage¶
void print_info(const Shape& s) {
std::cout << "Area: " << s.area()
<< " Perimeter: " << s.perimeter() << '\n';
}
// Polymorphism through base pointer/reference
std::vector<std::unique_ptr<Shape>> shapes;
shapes.push_back(std::make_unique<Circle>(0, 0, 5));
shapes.push_back(std::make_unique<Rectangle>(0, 0, 4, 6));
for (const auto& s : shapes) {
print_info(*s); // dispatches to correct area()/perimeter()
}
Access Specifiers in Inheritance¶
class Base {
private: int priv_; // only Base
protected: int prot_; // Base + derived
public: int pub_; // everyone
};
class Derived : public Base {
void foo() {
// priv_ = 1; // ERROR: private in Base
prot_ = 2; // OK: protected accessible in derived
pub_ = 3; // OK: public
}
};
// public inheritance: public stays public, protected stays protected
// protected inheritance: public+protected become protected
// private inheritance: everything becomes private
Interface Pattern (Abstract Base)¶
class ILogger {
public:
virtual ~ILogger() = default;
virtual void log(std::string_view msg) = 0;
virtual void flush() = 0;
};
class FileLogger : public ILogger {
std::ofstream file_;
public:
explicit FileLogger(const std::string& path) : file_(path) {}
void log(std::string_view msg) override { file_ << msg << '\n'; }
void flush() override { file_.flush(); }
};
class ConsoleLogger : public ILogger {
public:
void log(std::string_view msg) override { std::cout << msg << '\n'; }
void flush() override { std::cout.flush(); }
};
Multiple Inheritance and Diamond¶
// Diamond problem
class A { public: int val; };
class B : virtual public A {}; // virtual inheritance
class C : virtual public A {}; // virtual inheritance
class D : public B, public C {};
D d;
d.val = 42; // unambiguous: single A sub-object
override and final¶
class Base {
public:
virtual void process() const;
virtual void update();
};
class Derived : public Base {
public:
void process() const override; // OK: overrides Base::process
// void process() override; // ERROR: signature mismatch (missing const)
void update() override final; // overrides AND prevents further override
};
class Final final : public Derived { // cannot derive from Final
// void update() override; // ERROR: update() is final
};
Gotchas¶
- Issue: Missing
virtualdestructor in base class -> UB when deleting derived through base pointer -> Fix: Any class with virtual functions must havevirtual ~Base() = default; - Issue: Calling virtual function in constructor/destructor -> calls base version, not derived -> Fix: Don't call virtual functions in ctor/dtor. Use post-construction init or CRTP pattern.
- Issue: Forgetting
overridekeyword -> silent bug if base signature changes -> Fix: Always useoverrideon every overriding function - Issue: Object slicing - assigning derived to base by value loses derived data -> Fix: Use references or pointers for polymorphism, never value semantics