Skip to Content
Course content

191: Practice Exercise: Building a Shape Hierarchy with Area Calculations

Click on the "Edit" button in the top corner of the screen to edit your slide content.

A few years ago, I was reviewing code for a junior dev who was building a simple 2D layout tool. He had created separate classes for Rectangle and Circle, which was fine. The problem started when he needed to calculate the total area of all elements on the canvas. He had one std::vector for rectangles and another for circles. Every time he added a new shape—like a triangle or a pentagon—he had to create a new vector and add another for loop to his summation logic. It was a maintenance nightmare. I told him, "You're treating these as different species, but for the purposes of area, they're all just Shapes."

Defining the Abstract Contract

The trick to solving that mess is to stop caring about the specific type of shape when you're performing general operations. We do this by creating an abstract base class. In C++, this means defining a class with at least one pure virtual function. By declaring virtual double getArea() = 0; in a Shape class, you aren't providing an implementation—because a generic "shape" doesn't have a formula for area. Instead, you're creating a contract. Any class that inherits from Shape must implement getArea(), or the compiler will refuse to let you instantiate it.

class Shape {
public:
    virtual ~Shape() = default; // Always use a virtual destructor in hierarchies
    virtual double getArea() const = 0; 
};

I can't stress the virtual destructor enough. If you delete a Circle object through a Shape* pointer and the destructor isn't virtual, you're looking at undefined behavior and likely memory leaks. It's one of those C++ "gotchas" that will haunt your debugger for hours if you forget it.

Overriding Logic in Derived Classes

Once the contract is set, the derived classes handle the heavy lifting. The Rectangle class only cares about width and height; the Circle only cares about the radius. Because they override the same function signature, they can be treated interchangeably by the rest of your program. This is the essence of polymorphism.

When you implement these, remember to use the override keyword. It doesn't change how the code runs, but it tells the compiler, "I intend to override a virtual function from the base class." If you accidentally mistype the function name or get the const qualifier wrong, the compiler will scream at you immediately rather than silently creating a new, unrelated function.

Managing Heterogeneous Collections

This is where the magic actually happens. Instead of maintaining five different lists for five different shapes, you maintain one: a std::vector of pointers to the base class. Using std::unique_ptr is the professional way to handle this—it ensures that when the vector goes out of scope, all the derived objects are cleaned up properly without you having to manually call delete.

Now, your summation loop becomes a three-line affair. You iterate through the vector, call getArea() on each pointer, and add the result to a running total. The CPU figures out at runtime which version of the function to call based on the actual object type. Your code doesn't need to know if it's looking at a circle or a square; it just knows it's a Shape, and shapes have areas.




📋 Practical Task

Exercise: Building a Polymorphic Area Calculator for a 2D Layout Engine

Your task is to implement a shape hierarchy that allows a program to calculate the total area of a mixed collection of geometric objects.

  • Create an abstract base class named Shape with a virtual destructor and a pure virtual function double getArea() const.
  • Implement a Rectangle class that inherits from Shape. It should take width and height in its constructor.
  • Implement a Circle class that inherits from Shape. It should take a radius in its constructor (use 3.14159 for Pi).
  • Implement a Triangle class that inherits from Shape. It should take base and height in its constructor.
  • In your main function:
    • Create a std::vector of std::unique_ptr<Shape>.
    • Add at least one of each shape to the vector.
    • Iterate through the vector using a loop to calculate and print the total combined area of all shapes.
Rating
0 0

There are no comments for now.

to be the first to leave a comment.