What is an Abstract Class?
An abstract class is a class that cannot be instantiated on its own and is designed to be inherited by other classes. It serves as a blueprint for other classes and may contain abstract methods (methods without implementation) that derived classes must implement.
Purpose of Abstract Classes
Abstract classes are used for:
- Creating a common base class that provides a partial implementation
- Enforcing that derived classes implement specific methods
- Defining interfaces that derived classes must follow
- Providing a clear structure for hierarchical class relationships
How to Create an Abstract Class
In C++, a class becomes abstract when it has at least one pure virtual function. A pure virtual function is declared by using = 0 at the end of its declaration:
class AbstractClass {
public:
// Pure virtual function makes this an abstract class
virtual void pureVirtualFunction() = 0;
// Regular function with implementation
void regularFunction() {
cout << "This is a regular function" << endl;
}
};
Pure Virtual Functions
A pure virtual function:
- Is declared with the
virtualkeyword - Has
= 0at the end of its declaration - Has no implementation in the abstract class
- Must be implemented by any non-abstract derived class
class Shape {
public:
// Pure virtual function
virtual double area() = 0;
// Pure virtual function
virtual double perimeter() = 0;
// Regular virtual function with implementation
virtual void display() {
cout << "This is a shape" << endl;
}
};
Basic Example of an Abstract Class
#include <iostream>
using namespace std;
// Abstract class
class Shape {
public:
// Pure virtual functions
virtual double area() = 0;
virtual double perimeter() = 0;
// Regular method with implementation
void displayInfo() {
cout << "Area: " << area() << endl;
cout << "Perimeter: " << perimeter() << endl;
}
};
// Concrete derived class
class Rectangle : public Shape {
private:
double length;
double width;
public:
Rectangle(double l, double w) : length(l), width(w) {}
// Implementation of pure virtual function
double area() override {
return length * width;
}
// Implementation of pure virtual function
double perimeter() override {
return 2 * (length + width);
}
};
// Another concrete derived class
class Circle : public Shape {
private:
double radius;
public:
Circle(double r) : radius(r) {}
// Implementation of pure virtual function
double area() override {
return 3.14159 * radius * radius;
}
// Implementation of pure virtual function
double perimeter() override {
return 2 * 3.14159 * radius;
}
};
int main() {
// Shape shape; // Error: Cannot create an instance of an abstract class
Rectangle rect(5.0, 3.0);
Circle circle(4.0);
cout << "Rectangle information:" << endl;
rect.displayInfo();
cout << "\nCircle information:" << endl;
circle.displayInfo();
// Using pointers to the abstract class
Shape* shape1 = ▭
Shape* shape2 = &circle;
cout << "\nUsing pointers:" << endl;
cout << "Shape1 area: " << shape1->area() << endl;
cout << "Shape2 area: " << shape2->area() << endl;
return 0;
}
Output:
Rectangle information:
Area: 15
Perimeter: 16
Circle information:
Area: 50.2654
Perimeter: 25.1327
Using pointers:
Shape1 area: 15
Shape2 area: 50.2654
Key Characteristics of Abstract Classes
- Cannot be instantiated: You cannot create objects of an abstract class
- May contain pure virtual functions: Methods that derived classes must implement
- May contain regular functions: Methods with implementations that derived classes can use
- Can have constructors: Even though you can’t create objects directly
- Can have data members: Variables that derived classes inherit
- Can have pointers and references: You can create pointers and references to abstract classes
Abstract Class vs. Interface
In C++, there is no explicit “interface” keyword as in some other languages like Java or C#. Instead, abstract classes with only pure virtual functions serve as interfaces:
// This abstract class acts as an interface
class Drawable {
public:
virtual void draw() = 0;
virtual void resize() = 0;
// Virtual destructor
virtual ~Drawable() {}
};
The differences between a typical abstract class and an “interface-like” abstract class are:
-
Abstract class:
- May have implemented methods
- May have member variables
- Derived classes can only inherit from one abstract class (C++ limitation)
-
Interface-like abstract class:
- Contains only pure virtual functions
- Usually has no member variables
- Multiple interface-like classes can be inherited by a single class
Constructors in Abstract Classes
Even though you cannot create objects of an abstract class directly, abstract classes can have constructors. These constructors are called when a derived class object is created:
#include <iostream>
using namespace std;
class AbstractBase {
protected:
int value;
public:
// Constructor in abstract class
AbstractBase(int v) : value(v) {
cout << "AbstractBase constructor called with value " << value << endl;
}
// Pure virtual function
virtual void display() = 0;
// Virtual destructor
virtual ~AbstractBase() {
cout << "AbstractBase destructor called" << endl;
}
};
class Derived : public AbstractBase {
public:
// Call the base class constructor
Derived(int v) : AbstractBase(v) {
cout << "Derived constructor called" << endl;
}
// Implement the pure virtual function
void display() override {
cout << "Value in Derived: " << value << endl;
}
~Derived() {
cout << "Derived destructor called" << endl;
}
};
int main() {
Derived d(42);
d.display();
return 0;
}
Output:
AbstractBase constructor called with value 42
Derived constructor called
Value in Derived: 42
Derived destructor called
AbstractBase destructor called
Abstract Class Pointers and References
While you cannot create objects of an abstract class, you can create pointers and references to abstract classes. These pointers and references can then point to or refer to objects of derived classes:
#include <iostream>
#include <vector>
using namespace std;
class Animal {
public:
virtual void makeSound() = 0;
virtual void eat() {
cout << "Animal is eating" << endl;
}
};
class Dog : public Animal {
public:
void makeSound() override {
cout << "Woof!" << endl;
}
void eat() override {
cout << "Dog is eating bones" << endl;
}
};
class Cat : public Animal {
public:
void makeSound() override {
cout << "Meow!" << endl;
}
void eat() override {
cout << "Cat is eating fish" << endl;
}
};
void animalSound(Animal& animal) {
animal.makeSound();
}
int main() {
// Animal animal; // Error: Cannot instantiate abstract class
Dog dog;
Cat cat;
// Using abstract class reference
animalSound(dog); // Output: Woof!
animalSound(cat); // Output: Meow!
// Using abstract class pointers
Animal* animals[2];
animals[0] = &dog;
animals[1] = &cat;
for (int i = 0; i < 2; i++) {
animals[i]->makeSound();
animals[i]->eat();
}
// Using abstract class pointers with vector
vector<Animal*> animalVector;
animalVector.push_back(&dog);
animalVector.push_back(&cat);
for (Animal* a : animalVector) {
a->makeSound();
}
return 0;
}
Output:
Woof!
Meow!
Woof!
Dog is eating bones
Meow!
Cat is eating fish
Woof!
Meow!
Abstract Classes as Interfaces
In C++, abstract classes are often used to define interfaces that derived classes must implement:
#include <iostream>
#include <vector>
using namespace std;
// Database interface (abstract class)
class Database {
public:
virtual bool connect(const string& connectionString) = 0;
virtual bool disconnect() = 0;
virtual bool executeQuery(const string& query) = 0;
virtual ~Database() {}
};
// MySQL implementation
class MySQLDatabase : public Database {
public:
bool connect(const string& connectionString) override {
cout << "Connecting to MySQL database: " << connectionString << endl;
return true;
}
bool disconnect() override {
cout << "Disconnecting from MySQL database" << endl;
return true;
}
bool executeQuery(const string& query) override {
cout << "Executing MySQL query: " << query << endl;
return true;
}
};
// PostgreSQL implementation
class PostgreSQLDatabase : public Database {
public:
bool connect(const string& connectionString) override {
cout << "Connecting to PostgreSQL database: " << connectionString << endl;
return true;
}
bool disconnect() override {
cout << "Disconnecting from PostgreSQL database" << endl;
return true;
}
bool executeQuery(const string& query) override {
cout << "Executing PostgreSQL query: " << query << endl;
return true;
}
};
// Database client that works with any database implementation
class DatabaseClient {
private:
Database* db;
public:
DatabaseClient(Database* database) : db(database) {}
void performOperation() {
db->connect("server:1234");
db->executeQuery("SELECT * FROM users");
db->disconnect();
}
};
int main() {
MySQLDatabase mysqlDb;
PostgreSQLDatabase postgresDb;
// Client works with MySQL
DatabaseClient client1(&mysqlDb);
cout << "Working with MySQL:" << endl;
client1.performOperation();
cout << "\nWorking with PostgreSQL:" << endl;
// Same client works with PostgreSQL
DatabaseClient client2(&postgresDb);
client2.performOperation();
return 0;
}
Output:
Working with MySQL:
Connecting to MySQL database: server:1234
Executing MySQL query: SELECT * FROM users
Disconnecting from MySQL database
Working with PostgreSQL:
Connecting to PostgreSQL database: server:1234
Executing PostgreSQL query: SELECT * FROM users
Disconnecting from PostgreSQL database
Multiple Abstract Base Classes
A class can inherit from multiple abstract base classes, implementing all their pure virtual functions:
#include <iostream>
using namespace std;
// First abstract base class
class Drawable {
public:
virtual void draw() = 0;
virtual ~Drawable() {}
};
// Second abstract base class
class Movable {
public:
virtual void move(int x, int y) = 0;
virtual ~Movable() {}
};
// Third abstract base class
class Resizable {
public:
virtual void resize(int width, int height) = 0;
virtual ~Resizable() {}
};
// Class implementing multiple abstract base classes
class Rectangle : public Drawable, public Movable, public Resizable {
private:
int x, y, width, height;
public:
Rectangle(int x, int y, int w, int h) : x(x), y(y), width(w), height(h) {}
// Implement Drawable interface
void draw() override {
cout << "Drawing rectangle at (" << x << "," << y << ") "
<< "with width " << width << " and height " << height << endl;
}
// Implement Movable interface
void move(int newX, int newY) override {
x = newX;
y = newY;
cout << "Moved rectangle to (" << x << "," << y << ")" << endl;
}
// Implement Resizable interface
void resize(int newWidth, int newHeight) override {
width = newWidth;
height = newHeight;
cout << "Resized rectangle to width " << width
<< " and height " << height << endl;
}
};
int main() {
Rectangle rect(10, 20, 30, 40);
// Use object directly
rect.draw();
rect.move(15, 25);
rect.resize(50, 60);
rect.draw();
// Use through interfaces
Drawable* d = ▭
Movable* m = ▭
Resizable* r = ▭
d->draw();
m->move(100, 100);
r->resize(200, 200);
d->draw();
return 0;
}
Common Mistakes with Abstract Classes
-
Forgetting to implement a pure virtual function:
class Derived : public AbstractBase { // Error: Derived is still abstract because it doesn't implement all pure virtual functions }; -
Trying to instantiate an abstract class:
AbstractBase obj; // Error: Cannot instantiate abstract class -
Not making destructors virtual:
class AbstractBase { public: virtual void func() = 0; ~AbstractBase() {} // Should be virtual }; -
Forgetting to call the abstract class constructor:
class Derived : public AbstractBase { public: Derived() { // Should call AbstractBase constructor if it requires parameters // ... } };
Best Practices for Abstract Classes
- Make destructors virtual: Always make destructors virtual in abstract classes
- Use pure virtual functions for interface definition: Use pure virtual functions for methods that derived classes must implement
- Provide default implementations when appropriate: Use regular virtual functions for methods that have a common implementation
- Use abstract classes for common behavior: Group related functionality in abstract classes
- Keep interfaces small and focused: Abstract classes work best when they represent a single, well-defined concept
- Use multiple inheritance carefully: Inherit from multiple abstract classes only when necessary
Summary
Abstract classes in C++ provide a powerful way to define common interfaces and partial implementations that derived classes must follow and can extend. By using pure virtual functions, abstract classes ensure that concrete derived classes implement the required functionality. They serve as blueprints for derived classes and enable polymorphic behavior through base class pointers and references. Understanding how to design and use abstract classes effectively is essential for creating flexible, maintainable, and extensible object-oriented C++ code.