Abstract Classes and Pure Virtual Functions

Understanding Abstract Classes and Pure Virtual Functions

Welcome back! Previously, you delved into polymorphism and learned how to create more flexible code structures using classes and inheritance. In this session, we will take a step further and explore a crucial aspect of Object-Oriented Programming: abstract classes and pure virtual functions.

What You'll Learn

Abstract classes and pure virtual functions are essential when you want to define a common interface for a group of derived classes. They ensure that derived classes implement specific functions, enabling you to write more robust and scalable programs.

Let's revisit some of the key concepts through the following code example:

#include <iostream>
#include <string>

// Define an abstract class Shape. Note that the abstractness is achieved by having at least one pure virtual function denoted by = 0 in the declaration.
class Shape {
public:
    virtual ~Shape() = default; // Adding a virtual destructor
    virtual double area() const = 0; // Pure virtual function for calculating the area
    virtual double perimeter() const = 0; // Pure virtual function for calculating the perimeter
};

// Define a Circle class that inherits from Shape
class Circle : public Shape {
public:
    // Constructor to initialize the radius
    Circle(double radius) : radius(radius) {}

    // Implement the area and perimeter functions
    double area() const override { // Note, that the override keyword is optional
        return 3.14 * radius * radius;
    }

    double perimeter() const override {
        return 2 * 3.14 * radius;
    }

private:
    double radius;
};

// Define a Rectangle class that inherits from Shape
class Rectangle : public Shape {
public:
    // Constructor to initialize the width and height
    Rectangle(double width, double height) : width(width), height(height) {}

    // Implement the area and perimeter functions
    double area() const override {
        return width * height;
    }

    double perimeter() const override {
        return 2 * (width + height);
    }

private:
    double width;
    double height;
};

int main() {
    Circle circle(5);
    Rectangle rectangle(4, 6);

    std::cout << "Circle Area: " << circle.area() << ", Perimeter: " << circle.perimeter() << std::endl;
    std::cout << "Rectangle Area: " << rectangle.area() << ", Perimeter: " << rectangle.perimeter() << std::endl;

    // Using a Shape pointer to a Circle object
    Shape* shape = new Circle(3);
    std::cout << "Shape Area: " << shape->area() << ", Perimeter: " << shape->perimeter() << std::endl;
    delete shape; // Always remember to delete dynamically allocated memory

    return 0;
}

In this example, we define an abstract class Shape with two pure virtual functions: area and perimeter. Derived classes such as Circle and Rectangle implement these functions.

Let's now understand the abstract class and pure virtual functions in more detail:

An abstract class is a class that contains at least one pure virtual function - a function declared with = 0 at the end. An abstract class cannot be instantiated, but it can be used as a base class for other classes. In the example above, Shape is an abstract class.

The derived classes Circle and Rectangle inherit from the abstract class Shape. They must implement the pure virtual functions area and perimeter to provide concrete implementations. If a derived class does not implement all the pure virtual functions, it will also become an abstract class and cannot be instantiated as well.

Note, that it is important to provide a virtual destructor in the abstract class to ensure that the derived classes' destructors are called correctly when deleting objects through a base class pointer. This is achieved by adding virtual ~Shape() = default; in the Shape class.

Sign up

Join the 1M+ learners on CodeSignal

Be a part of our community of 1M+ users who develop and demonstrate their skills on CodeSignal