Intermediate · Operator overloading, inheritance and polymorphism · topic 20 of 42 · 25 min
Abstract classes, interfaces and good class design
Explanation
Pure virtual functions
Sometimes a base class cannot sensibly do the job itself. What is the area of a "Shape" in general? There is no answer. Writing = 0 after a virtual function declares it pure virtual: the base gives no implementation, and every concrete derived class must provide one.
class Shape {
public:
virtual ~Shape() = default;
virtual double area() const = 0; // pure virtual
};
// Shape s; // error: cannot create an object of abstract class Shape
class Square : public Shape {
public:
explicit Square(double side) : side_(side) {}
double area() const override { return side_ * side_; }
private:
double side_;
};
Square sq(3); // fine: Square implements area()Interfaces
Java and C# have an interface keyword. C++ does not; instead, an interface is an abstract class that has only pure virtual functions and a virtual destructor, and no data. Code that uses the interface depends only on "what can be done", not "how it is done". This makes it easy to swap implementations, for example a real SMS gateway in production and a fake one in tests.
class PaymentGateway { // an interface
public:
virtual ~PaymentGateway() = default;
virtual bool charge(const std::string& account, long long paise) = 0;
};
class Checkout {
public:
explicit Checkout(PaymentGateway& gw) : gw_(gw) {} // depends on the interface
bool placeOrder(const std::string& acc, long long paise) {
return paise > 0 && gw_.charge(acc, paise);
}
private:
PaymentGateway& gw_;
};
class FakeGateway : public PaymentGateway { // used in tests
public:
bool charge(const std::string&, long long) override { ++calls; return true; }
int calls = 0;
};Composition over inheritance, again
Deep inheritance trees (Vehicle → Car → ElectricCar → LuxuryElectricCar) become rigid: a change at the top ripples down everywhere. Many modern designs keep inheritance shallow: an interface at the top and concrete classes directly below it, with extra features added by holding other objects as members (composition).
Multiple inheritance, briefly
C++ allows a class to inherit from several bases: class Phone : public Camera, public MusicPlayer. Inheriting from several INTERFACES is common and safe. Inheriting data from several bases that share a common base leads to the "diamond problem" (two copies of the shared base), which C++ solves with virtual inheritance. As a learner, avoid diamonds; prefer interfaces plus composition.
Design checklist
- Does the derived class truly satisfy "is-a" everywhere the base is used? If not, use composition.
- Is the base class meant to be used polymorphically? Then give it a virtual destructor.
- Mark every override with override.
- Keep data private; give derived classes protected functions rather than protected data when you can.
- Hold polymorphic objects in std::unique_ptr<Base> (single owner) inside containers.
Need help?
Stuck on a word or an idea? Search this course, or every course, and jump straight to the explanation or a worked question.
Try:
Watch the lesson
Abstract classes, interfaces and good class design · 1/6
Some base classes cannot do the job themselves. They only describe what must be done.
Prefer a video by a teacher? Find free videos on “c++ abstract class pure virtual function interface design” on YouTube ↗
In real life: Swappable storage in a photo-backup app
A backup app can save photos to local disk, to a company server or to a cloud bucket. The upload logic depends on a Storage interface with save() and exists(). In tests, an InMemoryStorage implementation runs in milliseconds without touching the network; in production, the cloud implementation is plugged in.
#include <iostream>
#include <map>
#include <string>
class Storage {
public:
virtual ~Storage() = default;
virtual void save(const std::string& key, const std::string& data) = 0;
virtual bool exists(const std::string& key) const = 0;
};
class InMemoryStorage : public Storage {
public:
void save(const std::string& key, const std::string& data) override { files_[key] = data; }
bool exists(const std::string& key) const override { return files_.count(key) > 0; }
private:
std::map<std::string, std::string> files_;
};
void backup(Storage& store, const std::string& name, const std::string& bytes) {
if (!store.exists(name)) store.save(name, bytes);
}
int main() {
InMemoryStorage mem;
backup(mem, "IMG_001.jpg", "....");
std::cout << mem.exists("IMG_001.jpg") << '\n'; // 1
}Practice (0/4 done)
What makes a class abstract?
class A { public: virtual void f() = 0; virtual void g() = 0; }; class B : public A { public: void f() override {} }; Can you create B b;?
Which container correctly holds different shapes polymorphically?
Write an interface Logger with a pure virtual void log(const std::string&) and a virtual destructor. Implement ConsoleLogger (prints) and MemoryLogger (stores lines in a std::vector<std::string>). Write a class OrderService that takes a Logger& in its constructor and logs "order <id> placed". In main, use MemoryLogger and print how many lines were logged.
Quick quiz
Read more (free)
Related help
Before this, learn
This topic builds on these. If something here feels new, open them first.
Related topics
- Operator overloadingOperator overloading, inheritance and polymorphism · same module
- Inheritance and interfacesKotlin and Android App Development · Classes, objects and collections
- Inheritance: base and derived classesC# and .NET · Inheritance, interfaces and records
- Abstract classes and polymorphismC# and .NET · Inheritance, interfaces and records
- Abstract classesJava Programming (Core to Spring Boot) · Inheritance, polymorphism and interfaces
- Inheritance, interfaces, mixins and extensionsFlutter and Dart · Null safety and object-oriented Dart
Questions people ask about this
Open a question to see the answer and why. The first ones are this topic's own quiz, so try the quiz above before you peek.
How do you write an interface in C++?
Answer: As an abstract class with only pure virtual functions and a virtual destructor
C++ has no interface keyword; abstract classes play that role.
Why do interfaces make testing easier?
Answer: You can pass a fake implementation instead of a real network or database one
Code that depends on an interface can be tested with a fake or in-memory implementation.
What is the diamond problem?
Answer: A class getting two copies of a shared base through two inheritance paths
Virtual inheritance solves it; better still, avoid diamonds of data-carrying classes.
Can an abstract class have a constructor and data members?
Answer: Yes; it just cannot be instantiated on its own
Abstract only means "cannot be created directly". It may still hold shared data and constructors for derived classes.
Can an interface function have a default body in Kotlin?Related
How many base classes can a C# class have?Related
Answer: One
C# has single class inheritance; use interfaces for more abilities.
What must a non-abstract class do with an inherited abstract method?Related
Which base members can a derived class use directly?Related
Answer: public and protected
private members stay hidden even from derived classes.
Pass the quiz (60% or more) or mark this topic as done when you are ready.
Your progress is saved on this device only.
Next steps
- This is the last topic of “Operator overloading, inheritance and polymorphism”. The module exam comes next.
- Module 5 exam: Operators, inheritance and polymorphism · 13 questions · 25 min, without help. 4 topics of this module still to finish first.
- Then the next module, The Standard Template Library (STL). 42 topics left in the course before the final exam.