Menu
Coddy logo textTech

Abstract Classes

Part of the Object Oriented Programming section of Coddy's C++ journey — lesson 60 of 104.

An abstract class is any class that contains at least one pure virtual function. Because it has an incomplete interface, you cannot create objects of an abstract class directly - it exists solely to be inherited from.

class Shape {
public:
    virtual double area() = 0;      // Pure virtual - makes Shape abstract
    virtual double perimeter() = 0; // Another pure virtual function
    
    void printInfo() {              // Regular method - has implementation
        std::cout << "Area: " << area() << std::endl;
    }
    
    virtual ~Shape() = default;
};

Shape s;  // Error: cannot instantiate abstract class

Abstract classes can contain a mix of pure virtual functions (no implementation), regular virtual functions (with default implementation), and non-virtual functions. This allows you to define common behavior while requiring derived classes to implement specific functionality.

class Circle : public Shape {
    double radius;
public:
    Circle(double r) : radius(r) {}
    
    double area() override { return 3.14159 * radius * radius; }
    double perimeter() override { return 2 * 3.14159 * radius; }
};

Circle c(5.0);      // OK: Circle implements all pure virtual functions
c.printInfo();      // Uses inherited printInfo(), calls Circle's area()

A derived class must implement all pure virtual functions to become concrete (instantiable). If it leaves any unimplemented, it remains abstract itself. This mechanism ensures that every concrete class in your hierarchy provides the required functionality.

challenge icon

Challenge

Easy

Let's build a media player system that demonstrates how abstract classes define contracts for different media types. You'll create an abstract base class that establishes what every media player must do, then implement concrete players for audio and video content.

You'll organize your code across three files:

  • MediaPlayer.h: Define an abstract MediaPlayer class that serves as the blueprint for all media types. Your abstract class should have:
    • A protected std::string title member
    • A protected int duration member (in seconds)
    • A constructor that initializes both values
    • A pure virtual play() method — each media type will play differently
    • A pure virtual getMediaType() method returning a std::string
    • A regular (non-virtual) showInfo() method that prints: <title> (<duration>s) - Type: <mediaType> — this method should call getMediaType() to get the type
    • A virtual destructor
  • Players.h: Implement two concrete media players that inherit from MediaPlayer:

    AudioPlayer:

    • A private std::string artist member
    • A constructor taking title, duration, and artist
    • Implement play() to print: Playing audio: <title> by <artist>
    • Implement getMediaType() to return "Audio"

    VideoPlayer:

    • A private std::string resolution member
    • A constructor taking title, duration, and resolution
    • Implement play() to print: Playing video: <title> in <resolution>
    • Implement getMediaType() to return "Video"
  • main.cpp: Read four inputs (each on a separate line):
    1. Audio track title
    2. Audio duration (integer)
    3. Video title
    4. Video duration (integer)

    Create an AudioPlayer with artist "Unknown Artist" and a VideoPlayer with resolution "1080p". Store both in an array of MediaPlayer* pointers.

    Loop through the array and for each player, first call showInfo(), then call play(). Print a blank line between each media item. Clean up your dynamically allocated objects when done.

For example, with inputs Summer Vibes, 180, Nature Documentary, and 3600:

Summer Vibes (180s) - Type: Audio
Playing audio: Summer Vibes by Unknown Artist

Nature Documentary (3600s) - Type: Video
Playing video: Nature Documentary in 1080p

Notice how the showInfo() method is defined once in the abstract base class but calls the pure virtual getMediaType() — this demonstrates how abstract classes can mix concrete implementations with methods that derived classes must provide. Remember to use the override keyword on all overridden methods.

Cheat sheet

An abstract class contains at least one pure virtual function and cannot be instantiated directly. It exists to be inherited from and define an interface for derived classes.

Defining an Abstract Class

Use = 0 to declare a pure virtual function:

class Shape {
public:
    virtual double area() = 0;      // Pure virtual function
    virtual double perimeter() = 0; // Makes Shape abstract
    
    void printInfo() {              // Regular method with implementation
        std::cout << "Area: " << area() << std::endl;
    }
    
    virtual ~Shape() = default;     // Virtual destructor
};

Abstract classes can contain:

  • Pure virtual functions (no implementation)
  • Regular virtual functions (with default implementation)
  • Non-virtual functions

Implementing Concrete Classes

A derived class must implement all pure virtual functions to become instantiable:

class Circle : public Shape {
    double radius;
public:
    Circle(double r) : radius(r) {}
    
    double area() override { return 3.14159 * radius * radius; }
    double perimeter() override { return 2 * 3.14159 * radius; }
};

Circle c(5.0);      // OK: all pure virtual functions implemented
c.printInfo();      // Inherited method calls Circle's area()

If a derived class doesn't implement all pure virtual functions, it remains abstract and cannot be instantiated.

Try it yourself

#include <iostream>
#include <string>
#include "MediaPlayer.h"
#include "Players.h"

int main() {
    // Read inputs
    std::string audioTitle;
    int audioDuration;
    std::string videoTitle;
    int videoDuration;
    
    std::getline(std::cin, audioTitle);
    std::cin >> audioDuration;
    std::cin.ignore();
    std::getline(std::cin, videoTitle);
    std::cin >> videoDuration;
    
    // TODO: Create an AudioPlayer with artist "Unknown Artist"
    
    // TODO: Create a VideoPlayer with resolution "1080p"
    
    // TODO: Store both in an array of MediaPlayer* pointers
    
    // TODO: Loop through the array and for each player:
    //       1. Call showInfo()
    //       2. Call play()
    //       3. Print a blank line between each media item
    
    // TODO: Clean up dynamically allocated objects
    
    return 0;
}
quiz iconTest yourself

This lesson includes a short quiz. Start the lesson to answer it and track your progress.

All lessons in Object Oriented Programming