Abstract Classes

In C++, an abstract class is a class that cannot be instantiated directly and is designed to be used as a base class for other classes. An abstract class contains one or more pure virtual functions, which are functions that have no implementation in the base class and must be implemented in the derived classes.

To understand abstract classes, let's use a simple analogy.

Imagine a blueprint for a building. This blueprint isn't a building itself; you can't live in a blueprint, but it provides the basic design and rules for constructing a building. In programming, an abstract class is like this blueprint. It's a class that you can't use to create objects directly. Instead, it's meant to be a foundation or a guide for other classes.

An abstract class can contain 'abstract methods'. Think of these like incomplete parts of the blueprint, sections that say, "Something needs to be built here, but it's up to you to decide the specifics." These abstract methods are like placeholders that any class inheriting from the abstract class must fill out with their own implementation.

For example, if you have an abstract class named "Vehicle," it might have abstract methods like 'move' or 'fuelType'. You can't create a 'Vehicle' directly because it's too vague, but you can create specific classes like 'Car' or 'Bicycle' that use the 'Vehicle' blueprint and provide specific details for the 'move' and 'fuelType' methods.

In summary, abstract classes are like incomplete templates meant to be extended and completed by other classes. They set up a common structure that various derived classes can follow and customize.

Understanding Abstract Classes in C++

In object-oriented programming, an abstract class is a class that cannot be instantiated directly. Instead, it serves as a blueprint for other classes, providing a common structure and behavior for its derived classes. The primary role of an abstract class is to define a common interface that its subclasses must follow.

What Makes a Class Abstract?

An abstract class typically contains one or more pure virtual functions. A pure virtual function is a function declared in a base class without implementation, signaling that any derived class must provide its own implementation. In C++, a pure virtual function is declared by assigning it a value of 0.

Conceptual Example: Shape

Imagine a base class called Shape, which could contain attributes and behaviors common to all shapes (e.g., color, name). However, a function like calculateArea() would depend on the specific shape. The base class Shape cannot define this function meaningfully, but it can enforce that all derived classes must implement it.

Now, you can define derived classes like Circle, Rectangle, and Triangle that inherit from Shape and implement calculateArea() according to the mathematical rules for each shape.

Benefits of Abstract Classes

Abstract classes provide a clear, flexible framework for organizing code. They allow you to work with general concepts (e.g., "Shape") without knowing the specific type (e.g., "Circle" or "Rectangle"), promoting code reuse and reducing redundancy.

Defining an Abstract Class

To define an abstract class in C++, you declare one or more pure virtual functions. A pure virtual function is indicated by appending = 0 to the function declaration.

Example: Abstract Class Shape

class Shape { public: virtual void draw() = 0; // Pure virtual function };

In this example:

  • Shape is an abstract class because it has a pure virtual function draw().

  • You cannot create objects of type Shape because it lacks an implementation for draw().

To use this abstract class, you must derive a subclass and implement the pure virtual function.

Derived Class Example

class Circle : public Shape { public: void draw() override { cout << "Drawing a circle" << endl; } }; class Rectangle : public Shape { public: void draw() override { cout << "Drawing a rectangle" << endl; } };

Here:

  • Circle and Rectangle are derived classes that provide their own implementation of the draw() function.

  • Since these classes implement all the pure virtual functions from Shape, they are concrete classes that can be instantiated.

Using Abstract Classes in Practice

Now, you can create objects of Circle and Rectangle, but treat them as Shape objects:

int main() { Shape* shape1 = new Circle(); Shape* shape2 = new Rectangle(); shape1->draw(); // Output: "Drawing a circle" shape2->draw(); // Output: "Drawing a rectangle" delete shape1; delete shape2; return 0; }

In this code:

  • shape1 and shape2 are pointers to Shape, but they point to objects of Circle and Rectangle.

  • Because draw() is a virtual function, the correct implementation is chosen at runtime based on the actual type of the object (either Circle or Rectangle).

Key Features of Abstract Classes

  1. Pure Virtual Functions: Abstract classes define pure virtual functions that must be implemented by derived classes. This ensures that derived classes follow a consistent interface.

  2. Cannot Instantiate Abstract Classes: You cannot create objects of an abstract class because they lack complete implementation. Abstract classes are designed to be subclassed.

  3. Provides Common Interface: Abstract classes serve as a common interface for multiple derived classes. They allow you to interact with different derived classes in a unified way, without knowing the specific details of each class.

Advantages of Abstract Classes

  • Enforce Design Contracts: Abstract classes establish a "contract" for derived classes, ensuring they implement specific behaviors (e.g., draw() in all shapes).

  • Polymorphism Support: By using base class pointers or references, you can call functions on derived objects, enabling runtime polymorphism.

  • Modularity and Code Reusability: Abstract classes allow for partial code reuse. Common functionality can be implemented in the abstract class, while specific behaviors are left for derived classes to define.

  • Prevents Instantiation of General Classes: Abstract classes prevent the creation of objects that don’t make sense on their own. For instance, you don’t want a generic Shape object, but rather specific shapes like Circle or Rectangle.

Abstract Classes in Real-World Scenarios

Consider an abstract class GameObject in a game engine. It might have pure virtual functions like update() and draw(). You can then create specific derived classes for different game objects, such as Player, Enemy, and PowerUp, each implementing update() and draw() differently.

This allows the game engine to treat all game objects uniformly, while still enabling each object to have its own unique behavior.

Virtual Destructors

In abstract classes, it’s common to declare a virtual destructor to ensure proper cleanup of resources when a derived class object is deleted via a base class pointer.

Example:

Without a virtual destructor, deleting an object of a derived class through a pointer to the base class can result in undefined behavior or memory leaks, as the derived class's destructor might not be called.

Abstract classes in C++ play a crucial role in designing flexible and reusable code. They define a common interface for derived classes, enforce specific behaviors through pure virtual functions, and support polymorphism. Abstract classes provide structure, modularity, and consistency in your code, making them essential tools in object-oriented programming.

 

2024 - Programming 3 / Data Structures - Author: Dr. Kevin Roark