Interfaces in C++ (Abstract Base Classes)

An interface is typically represented using an Abstract Base Class (ABC). An abstract base class is a class that contains at least one pure virtual function. Abstract classes cannot be instantiated directly and are intended to be subclassed by other classes that implement the pure virtual functions. This concept allows C++ to provide a similar behavior to interfaces found in other programming languages like Java or C#.

Key Concepts

  1. Abstract Base Class: A class that has one or more pure virtual functions. It serves as a blueprint for derived classes, which must implement the pure virtual functions.
  2. Pure Virtual Function: A virtual function that has no implementation in the base class and is defined using the = 0 syntax.
  3. Interface-like Behavior: C++ doesn’t have a direct “interface” keyword like some other languages. Instead, abstract base classes with only pure virtual functions serve as interfaces.

Pure Virtual Function

A pure virtual function is declared in the base class but has no implementation. Derived classes must provide an implementation for these functions in order to be instantiated.

Syntax for declaring a pure virtual function:

virtual return_type function_name(parameters) = 0;

The = 0 syntax marks the function as pure virtual, making the containing class abstract.

Example of an Abstract Base Class (ABC)

class Shape {
public:
    // Pure virtual function making this class abstract
    virtual void draw() const = 0;  
    virtual double area() const = 0; 
    virtual ~Shape() {}  // Virtual destructor for proper cleanup
};

In this example, the Shape class has two pure virtual functions: draw() and area(). These functions must be implemented by any class that inherits from Shape, and Shape cannot be instantiated directly.

Implementing an Interface (Derived Class)

A derived class must provide definitions for all pure virtual functions of the base class. Once all pure virtual functions are defined, the derived class becomes concrete and can be instantiated.

class Circle : public Shape {
private:
    double radius;
public:
    Circle(double r) : radius(r) {}

    // Implement the pure virtual functions from Shape
    void draw() const override {
        std::cout << "Drawing a circle with radius " << radius << std::endl;
    }

    double area() const override {
        return 3.14159 * radius * radius;
    }
};

In this example, the Circle class inherits from Shape and implements the draw() and area() functions, making it a concrete class that can be instantiated.

int main() {
    Shape* shape = new Circle(5.0);  // Polymorphic behavior
    shape->draw();                   // Calls Circle's draw function
    std::cout << "Area: " << shape->area() << std::endl;

    delete shape;                     // Clean up
    return 0;
}

Why Use Abstract Base Classes?

Polymorphism: Abstract base classes provide the foundation for polymorphism. You can write code that operates on pointers or references to the abstract base class, enabling flexibility in the types of objects used at runtime.

void renderShape(const Shape& shape) {
    shape.draw();  // Calls the derived class' implementation
}

Code Reusability: By defining common functionality or behavior in the abstract base class and allowing derived classes to implement specific behaviors, you can create a framework for reusability.

Enforcing a Contract: Abstract base classes enforce that derived classes provide certain behavior by defining pure virtual functions. Any class derived from an abstract class must implement the abstract methods, ensuring that the derived class adheres to a certain “contract.”

    Example: Using Interfaces for Multiple Shapes

    class Rectangle : public Shape {
    private:
        double width, height;
    public:
        Rectangle(double w, double h) : width(w), height(h) {}
    
        void draw() const override {
            std::cout << "Drawing a rectangle with width " << width << " and height " << height << std::endl;
        }
    
        double area() const override {
            return width * height;
        }
    };
    
    int main() {
        Shape* shapes[] = {new Circle(3.0), new Rectangle(4.0, 5.0)};
    
        for (int i = 0; i < 2; ++i) {
            shapes[i]->draw();
            std::cout << "Area: " << shapes[i]->area() << std::endl;
            delete shapes[i];  // Clean up memory
        }
        return 0;
    }

    Here, we have two different classes (Circle and Rectangle) both inheriting from the abstract Shape class. Both classes implement draw() and area(). In the main() function, polymorphism allows the use of the Shape* pointer to hold objects of both derived types and call their methods.

    Characteristics of Abstract Base Classes in C++

    1. Cannot be Instantiated: You cannot create an instance of an abstract class directly. For example, Shape s; will cause a compile-time error.
    2. Polymorphism: Abstract base classes can be used with polymorphism. When you have a pointer or reference to a base class (Shape*), you can work with derived objects without needing to know their specific type.
    3. Virtual Destructors: It’s important to declare a virtual destructor in an abstract base class. This ensures that the destructors of derived classes are called correctly when deleting objects via base class pointers.
      virtual ~Shape() {} // Virtual destructor
    4. Inheritance: Derived classes can implement or extend functionality from the base class. If a derived class does not implement all pure virtual functions, it remains abstract and cannot be instantiated.
    5. Multiple Inheritance: C++ allows multiple inheritance, which means a class can implement multiple abstract base classes, giving it behavior from several interfaces.

    Summary

    • Abstract Base Classes (ABCs) in C++ act as interfaces when they contain pure virtual functions. Derived classes must provide implementations for these functions.
    • Pure virtual functions are declared using the = 0 syntax and must be implemented by derived classes.
    • Polymorphism: Abstract base classes enable polymorphism by allowing objects of derived classes to be manipulated through base class pointers or references.
    • Virtual destructors are important in abstract classes to ensure proper resource cleanup in derived classes.