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 classAbstract 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
EasyLet'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 abstractMediaPlayerclass that serves as the blueprint for all media types. Your abstract class should have:- A protected
std::string titlemember - A protected
int durationmember (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 astd::string - A regular (non-virtual)
showInfo()method that prints:<title> (<duration>s) - Type: <mediaType>— this method should callgetMediaType()to get the type - A virtual destructor
- A protected
Players.h: Implement two concrete media players that inherit fromMediaPlayer:AudioPlayer:- A private
std::string artistmember - 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 resolutionmember - A constructor taking title, duration, and resolution
- Implement
play()to print:Playing video: <title> in <resolution> - Implement
getMediaType()to return"Video"
- A private
main.cpp: Read four inputs (each on a separate line):- Audio track title
- Audio duration (integer)
- Video title
- Video duration (integer)
Create an
AudioPlayerwith artist"Unknown Artist"and aVideoPlayerwith resolution"1080p". Store both in an array ofMediaPlayer*pointers.Loop through the array and for each player, first call
showInfo(), then callplay(). 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 1080pNotice 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;
}
This lesson includes a short quiz. Start the lesson to answer it and track your progress.
All lessons in Object Oriented Programming
1Fundamentals of OOP
External FilesC++ Build & CompilationHeader Files & Source FilesNamespaces & ScopeIntroduction to OOP in C++Classes vs ObjectsThe 'this' PointerMethods (Member Functions)Attributes (Data Members)Ctors & Dtors BasicsRecap - Simple Calculator4Class Properties
Instance vs Static MembersGetters and SettersConst Member FunctionsMutable KeywordStatic Methods and VariablesFriend Functions & ClassesRecap - Bank Account Manager7Inheritance
Basic InheritanceInheritance Access LevelsCtor & Dtor Call OrderMethod OverridingVirtual Functions & VTableMultiple InheritanceVirtual InheritanceRecap - Employee Hierarchy2Memory Management
Stack vs Heap MemoryPointers and ReferencesDynamic Memory (new/delete)Smart Pointers in C++RAII in C++Recap - Dynamic Array Manager5Encapsulation
Access Specifiers in C++Access Specifiers In DepthInformation HidingStruct vs ClassNested & Inner ClassesRecap - Student Records System8Polymorphism
Compile vs Runtime PolymorphFunction OverloadingVirtual Functions RevisitedPure Virtual FunctionsAbstract ClassesInterface Design in C++Dynamic Casting & RTTIRecap - Shape Calculator3Constructors & Destructors
Default ConstructorParameterized ConstructorCopy ConstructorMove ConstructorConstructor Init ListsDelegating ConstructorsDestructor Deep DiveRule of Three / Five / ZeroRecap - String Class6Operator Overloading
Intro to Operator OverloadArithmetic Operator OverloadComparison Operator OverloadStream OperatorsAssignment Operator Overload[] and () Operator OverloadType Conversion OperatorsRecap - Matrix Class9Templates
Function TemplatesClass TemplatesTemplate SpecializationVariadic TemplatesSFINAE & Type Traits BasicsRecap - Generic Container