Question

How to Declare an Interface in C++: Abstract Base Classes Explained

cppinheritanceinterfaceabstract-classpure-virtual

Question

How do you set up a class in C++ that represents an interface? Is an interface in C++ simply an abstract base class?

Short Answer

By the end of this page, you will understand how C++ represents interfaces using abstract base classes, what pure virtual functions do, why virtual destructors matter, and how concrete classes implement those interfaces in real code.

Concept

In C++, there is no separate interface keyword like in some other languages such as Java or C#. Instead, the usual way to model an interface is to create an abstract base class.

A class becomes abstract when it contains at least one pure virtual function. A pure virtual function is declared by assigning = 0 at the end of the function declaration.

class Shape {
public:
    virtual double area() const = 0;
};

This means:

  • Shape cannot be instantiated directly.
  • Any derived class must provide an implementation for area() unless it is also abstract.
  • The class acts as a contract: derived classes promise to provide certain behavior.

In practice, an interface-style class in C++ usually has:

  • only pure virtual functions
  • no data members
  • a virtual destructor

For example:

class ILogger {
public:
    virtual ~ILogger() = default;
    virtual void log(const std::string& message) = 0;
};

This pattern matters because it lets you write code against a common contract instead of a specific implementation. That makes code easier to extend, test, and reuse.

For example, code can depend on ILogger without caring whether the real object writes to a file, the console, or a remote service.

Although an interface in C++ is usually an abstract base class, not every abstract base class is used purely as an interface. Some abstract base classes also contain shared implementation or member data. So the key idea is:

  • Interface-style class: abstract base class used mainly as a contract
  • General abstract base class: abstract class that may also provide reusable implementation

Mental Model

Think of an interface in C++ like a job description.

The interface says:

  • what tasks must be done
  • what function names and signatures must exist
  • but not exactly how the work is performed

For example, a Printer interface might say every printer must support print().

A laser printer and a PDF printer both satisfy the same job description, but they do the work differently.

So:

  • the interface is the contract
  • the derived class is the worker fulfilling that contract
  • the pure virtual functions are the required tasks

This is why abstract base classes are used for interfaces in C++: they define required behavior without forcing one specific implementation.

Take Quiz

Syntax and Examples

Basic interface-style class in C++

class IAnimal {
public:
    virtual ~IAnimal() = default;
    virtual void speak() const = 0;
};

What this means

  • IAnimal is abstract because speak() is pure virtual.
  • virtual ~IAnimal() = default; makes destruction safe through a base-class pointer.
  • You cannot create an IAnimal directly.

Implementing the interface

#include <iostream>
using namespace std;

class IAnimal {
public:
    virtual ~IAnimal() = default;
    virtual void speak() const = 0;
};

class Dog : public IAnimal {
public:
    void speak() const override {
        cout << "Woof\n";
    }
};

int main() {
    Dog d;
    d.speak();
}

Using polymorphism

#include <iostream>
#include <memory>
using namespace std;

class IAnimal {
public:
    virtual ~IAnimal() = default;
    virtual void speak() const = 0;
};

class Dog : public IAnimal {
public:
    void speak() const override {
        cout << "Woof\n";
    }
};

class Cat : public IAnimal {
public:
    void speak() const override {
        cout << "Meow\n";
    }
};

int main() {
    unique_ptr<IAnimal> a1 = make_unique<Dog>();
    unique_ptr<IAnimal> a2 = make_unique<Cat>();

    a1->speak();
    a2->speak();
}

Here, both Dog and Cat can be treated as IAnimal. The calling code uses the interface without needing to know the exact class.

Interface with multiple functions

class IShape {
public:
    virtual ~IShape() = default;
    virtual double area() const = 0;
    virtual double perimeter() const = 0;
};

Any class implementing IShape must provide both functions.

Step by Step Execution

Consider this example:

#include <iostream>
using namespace std;

class IAnimal {
public:
    virtual ~IAnimal() = default;
    virtual void speak() const = 0;
};

class Dog : public IAnimal {
public:
    void speak() const override {
        cout << "Woof\n";
    }
};

int main() {
    IAnimal* animal = new Dog();
    animal->speak();
    delete animal;
}

Step-by-step

  1. IAnimal is declared with a pure virtual function speak().

    • Because of = 0, IAnimal is abstract.
    • You cannot write IAnimal animal;
  2. Dog inherits from IAnimal.

    • Dog provides an implementation of speak().
    • That makes Dog a concrete class.
  3. In main(), new Dog() creates a Dog object.

  4. The pointer type is IAnimal*.

    • This is important: the code stores a Dog as a pointer to the interface.
    • This allows code to work with any class implementing IAnimal.
  5. animal->speak(); calls the Dog version of speak().

    • Because the function is virtual, C++ uses dynamic dispatch.
    • Output: Woof
  6. delete animal; destroys the object.

    • Because the destructor in IAnimal is virtual, the correct derived destructor would run.
    • This prevents undefined behavior and resource leaks in real programs.

Key lesson

The interface defines the required behavior, and virtual dispatch makes the correct derived implementation run at runtime.

Real World Use Cases

Interfaces in C++ are commonly used when multiple classes should provide the same behavior in different ways.

Common examples

  • Logging systems

    • ILogger can be implemented by ConsoleLogger, FileLogger, or NetworkLogger.
  • Payment providers

    • IPaymentProcessor can be implemented by Stripe, PayPal, or a mock test processor.
  • Game engines

    • IRenderer might have OpenGL, Vulkan, or DirectX implementations.
  • Device drivers

    • A common interface can describe devices that read data, while each device class handles hardware-specific details.
  • Testing and mocking

    • If your code depends on an interface, you can replace the real class with a fake or mock version in tests.

Example scenario

class IDataStore {
public:
    virtual ~IDataStore() = default;
    virtual void save(const std::string& value) = 0;
};

Possible implementations:

  • save to a file
  • save to a database
  • save to memory for testing

The rest of the code only depends on IDataStore, not on one storage method.

Take Quiz

Real Codebase Usage

In real projects, interface-style classes are often used to separate what a system does from how it does it.

Common patterns

Dependency injection

A class receives an interface instead of creating a concrete dependency directly.

class ILogger {
public:
    virtual ~ILogger() = default;
    virtual void log(const std::string& message) = 0;
};

class Service {
private:
    ILogger& logger;

public:
    Service(ILogger& logger) : logger(logger) {}

    void run() {
        logger.log("Service started");
    }
};

This makes Service easier to test and reuse.

Testing with fake implementations

class FakeLogger : public ILogger {
public:
    void log(const std::string& message) override {
        // store message for test verification
    }
};

Plug-in style architecture

Applications may define interfaces for modules, then load different implementations depending on configuration.

Stable API boundaries

Interfaces help large codebases keep modules loosely coupled. One team can change an implementation without forcing all callers to change.

Design notes developers often follow

  • Prefer small, focused interfaces.
  • Add a virtual destructor.
  • Use override in derived classes.
  • Avoid putting unnecessary data in interface-only classes.
  • Pass interfaces by reference or pointer when polymorphism is needed.

Common Mistakes

1. Forgetting the virtual destructor

This is one of the most important mistakes.

Broken code

class IAnimal {
public:
    virtual void speak() const = 0;
};

If you delete a derived object through an IAnimal*, behavior is unsafe because the base destructor is not virtual.

Correct code

class IAnimal {
public:
    virtual ~IAnimal() = default;
    virtual void speak() const = 0;
};

2. Trying to instantiate the interface

Broken code

IAnimal animal;

This fails because IAnimal is abstract.

Fix

Instantiate a concrete derived class instead.

Dog dog;

3. Forgetting to implement all pure virtual functions

Broken code

class IShape {
public:
    virtual ~IShape() = default;
    virtual double area() const = 0;
};

class Circle : public IShape {
    // area() missing
};

Circle is still abstract if it does not implement area().

4. Signature mismatch when overriding

Broken code

class IAnimal {
public:
    virtual ~IAnimal() = default;
    virtual void speak() const = 0;
};

class Dog : public IAnimal {
public:
    void speak() override {
        // missing const
    }
};

This does not correctly override the base function.

Fix

class Dog : public IAnimal {
public:
    void speak() const override {
    }
};

Using override helps the compiler catch mistakes.

5. Adding data members to what should be a pure interface

This is not always wrong, but if your goal is a clean interface, adding state can make the class less flexible.

6. Confusing abstract base classes with interface-only classes

An abstract base class may include shared code and member variables. An interface-style class is usually kept minimal and contract-focused.

Comparisons

ConceptWhat it meansTypical contentsCan contain implementation?Main use
Interface-style class in C++An abstract base class used as a contractPure virtual functions, virtual destructorUsually noDefine required behavior
Abstract base classA class that cannot be instantiatedOne or more pure virtual functions, sometimes data and concrete methodsYesShared design and reusable base behavior
Concrete classA fully implemented classNormal methods and data membersYesCreate actual objects

Interface-style class vs abstract base class

  • Every interface-style class in C++ is typically an abstract base class.
  • But not every abstract base class is a pure interface.
  • Some abstract base classes include common logic for derived classes.

Pure virtual vs regular virtual

Function typeMeaning
virtual void f();Can be overridden, but does not force derived classes to override
virtual void f() = 0;Must be implemented by concrete derived classes

C++ vs languages with an interface keyword

Language styleHow interfaces are declared
C++Usually with an abstract base class and pure virtual functions
Java/C#With a dedicated interface keyword

So the idea is similar, but the syntax is different in C++.

Take Quiz

Cheat Sheet

Quick reference

Declare an interface-style class

class IExample {
public:
    virtual ~IExample() = default;
    virtual void doWork() = 0;
};

Implement it

class Example : public IExample {
public:
    void doWork() override {
        // implementation
    }
};

Rules to remember

  • C++ has no built-in interface keyword.
  • Use an abstract base class to model an interface.
  • A class becomes abstract when it has at least one pure virtual function.
  • Pure virtual syntax: virtual ReturnType name(...) = 0;
  • Add a virtual destructor for safe polymorphic deletion.
  • Use override in derived classes.
  • You cannot instantiate an abstract class directly.

Common pattern

class IService {
public:
    virtual ~IService() = default;
    virtual void run() = 0;
};

Best practices

  • Keep interfaces small.
  • Prefer behavior-only members.
  • Pass interface objects by reference or pointer.
  • Use smart pointers when ownership is involved.

Edge case

A pure virtual destructor is allowed, but it still needs a definition if used:

class Base {
public:
    virtual ~Base() = 0;
};

Base::~Base() {}

Usually, virtual ~Base() = default; is simpler.

FAQ

Is there an interface keyword in C++?

No. C++ usually models interfaces with abstract base classes containing pure virtual functions.

Is an interface in C++ just an abstract base class?

Usually yes. More specifically, an interface-style class is an abstract base class used mainly to define a contract.

What makes a class abstract in C++?

A class is abstract if it has at least one pure virtual function, written with = 0.

Why should an interface have a virtual destructor?

So deleting an object through a base-class pointer works correctly and safely.

Can an abstract base class contain normal method implementations?

Yes. An abstract base class can contain both pure virtual and fully implemented methods. If it does, it is not a pure interface-only design.

Can an interface in C++ have member variables?

It can, but interface-style classes usually avoid data members and focus only on behavior.

Should I use override when implementing interface methods?

Yes. It helps the compiler detect signature mismatches and makes code clearer.

Take Quiz

Mini Project

Description

Create a small notification system where different classes send messages in different ways, but all follow the same interface. This demonstrates how C++ interfaces let you write one piece of code that works with many implementations.

Goal

Build an interface-style class for notifications and implement at least two concrete notification types that can be used through the same base interface.

Requirements

  • Create an abstract base class named INotifier
  • Add a pure virtual function that sends a message
  • Add a virtual destructor to the interface
  • Implement at least two classes such as EmailNotifier and SmsNotifier
  • Write a function that accepts the interface and sends a message through it
Take Quiz

Keep learning

Related questions

View more