Polymorphism
Okay, picture this: you've got an array of Shape* pointers, and each one is secretly pointing to a different shape — a Circle, a Rectangle, a Triangle. You loop through them and call shape->area() on every single one. Here's the cool part: the right version of area() runs for each shape, even though your pointer type is just Shape* for all of them. How does that work? Welcome to polymorphism — a Greek word that literally means "many forms." And honestly? It's probably the most powerful idea in all of object-oriented programming.
But how does C++ actually figure out which area() to call at runtime? Think of it like this: imagine every class carries a little cheat sheet — a hidden lookup table that says "when someone calls area() on me, here's the actual function to run." That cheat sheet is called a vtable (virtual table), and the whole process of checking it at runtime is called dynamic dispatch. The keyword that kicks all of this off? virtual. Without it in your base class, C++ just blindly calls the base version. With virtual, it checks the actual object and picks the right function. Pretty neat, right?
Now, what if the base class concept is too vague to even have a real implementation? Like, what's the area of a generic "shape"? That doesn't even make sense. So you mark the function as pure virtual with = 0 (like virtual double area() const = 0;), and now the class becomes abstract — you literally cannot create objects of it. It's basically a contract that says "any class inheriting from me MUST fill in these blanks." Oh, and one more critical thing you need to know: virtual destructors. If you delete a derived object through a base pointer without one, only the base class destructor runs — meaning your derived class's cleanup code never executes, and you've got yourself a resource leak.
| Concept | What It Means | Example |
|---|---|---|
| Virtual function | A function that uses dynamic dispatch to call the correct derived version at runtime | virtual double area() const; |
| Pure virtual function | A function with no implementation in the base class — derived classes MUST override it | virtual double area() const = 0; |
| Abstract class | A class with at least one pure virtual function — cannot be instantiated | Shape with area() = 0 |
| Dynamic dispatch | The runtime mechanism that looks up the vtable to call the correct function | shape_ptr->area() calls Circle's or Rect's version |
| Virtual destructor | Ensures the derived class destructor runs when deleting through a base pointer | virtual ~Shape() {} |
So why does all of this matter? Because polymorphism lets you write code that works with Shape* pointers, and it'll automatically handle any shape type — even ones that haven't been created yet. You're essentially coding against an interface, not a specific implementation, and that's a massive deal in real-world software.