Lesson 5
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:

C++
1#include <iostream> 2#include <string> 3 4// 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. 5class Shape { 6public: 7 virtual ~Shape() = default; // Adding a virtual destructor 8 virtual double area() const = 0; // Pure virtual function for calculating the area 9 virtual double perimeter() const = 0; // Pure virtual function for calculating the perimeter 10}; 11 12// Define a Circle class that inherits from Shape 13class Circle : public Shape { 14public: 15 // Constructor to initialize the radius 16 Circle(double radius) : radius(radius) {} 17 18 // Implement the area and perimeter functions 19 double area() const override { // Note, that the override keyword is optional 20 return 3.14 * radius * radius; 21 } 22 23 double perimeter() const override { 24 return 2 * 3.14 * radius; 25 } 26 27private: 28 double radius; 29}; 30 31// Define a Rectangle class that inherits from Shape 32class Rectangle : public Shape { 33public: 34 // Constructor to initialize the width and height 35 Rectangle(double width, double height) : width(width), height(height) {} 36 37 // Implement the area and perimeter functions 38 double area() const override { 39 return width * height; 40 } 41 42 double perimeter() const override { 43 return 2 * (width + height); 44 } 45 46private: 47 double width; 48 double height; 49}; 50 51int main() { 52 Circle circle(5); 53 Rectangle rectangle(4, 6); 54 55 std::cout << "Circle Area: " << circle.area() << ", Perimeter: " << circle.perimeter() << std::endl; 56 std::cout << "Rectangle Area: " << rectangle.area() << ", Perimeter: " << rectangle.perimeter() << std::endl; 57 58 // Using a Shape pointer to a Circle object 59 Shape* shape = new Circle(3); 60 std::cout << "Shape Area: " << shape->area() << ", Perimeter: " << shape->perimeter() << std::endl; 61 delete shape; // Always remember to delete dynamically allocated memory 62 63 return 0; 64}

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.

Why It Matters

Abstract classes and pure virtual functions offer a way to enforce certain patterns and rules in your code. They allow you to design a system where different types of objects can be treated uniformly while ensuring that specific behaviors are implemented in each derived class.

By mastering abstract classes and pure virtual functions, you'll be able to:

  1. Create more organized and readable code: You'll have a clear structure that dictates how certain functions should behave.
  2. Encourage code reusability: Common code can reside in abstract base classes, reducing redundancy.
  3. Enhance flexibility: Easily add new types of derived classes without modifying existing code — a key principle of software design.

Intrigued? Let's move on to the practice section and solidify these concepts together. You're on your way to becoming proficient in building sophisticated and maintainable systems!

Enjoy this lesson? Now it's time to practice with Cosmo!
Practice is how you turn knowledge into actual skills.