Introduction
Sometimes a base class cannot sensibly do the job itself: what is the area of a "shape" in general? Declare the function pure virtual with = 0: it has no body, and every concrete derived class must override it.
Some base classes exist only to describe what every derived class must do. Making them abstract lets the compiler enforce that, so that no one forgets to supply a part.
How it works
Pure virtual functions
#include <iostream>
#include <string>
using namespace std;
class Shape {
public:
virtual double area() const = 0;
virtual string name() const = 0;
void describe() const {
cout << name() << " with area " << area() << endl;
}
virtual ~Shape() {}
};
class Rectangle : public Shape {
double w, h;
public:
Rectangle(double w, double h) : w(w), h(h) {}
double area() const override {
return w * h;
}
string name() const override {
return "rectangle";
}
};
int main() {
Rectangle r(2, 3);
Shape& s = r;
s.describe();
return 0;
}virtual double area() const = 0;makesShapean abstract class. You cannot create aShapeobject:Shape s;is a compile error. You can still haveShape *andShape &, pointing to derived objects.Rectangleoverrides every pure virtual function, so it is concrete: it can be created.Shape::describeis an ordinary member function that calls the pure virtualarea(). Each derived class fills in the missing piece. This pattern (fixed steps in the base class, details in derived classes) is called the template method.
Syntax
class Shape {
public:
virtual double area() const = 0; // pure virtual
virtual ~Shape() {}
};
Example
#include <iostream>
#include <string>
using namespace std;
class Printable {
public:
virtual void print() const = 0;
virtual ~Printable() {}
};
class Document {
protected:
string title;
public:
Document(string t) : title(t) {}
};
class Report : public Document, public Printable {
int pages;
public:
Report(string t, int p) : Document(t), pages(p) {}
void print() const override {
cout << "Report: " << title << " (" << pages << " pages)" << endl;
}
};
void printTwice(const Printable& p) {
p.print();
p.print();
}
int main() {
Report r("Lab results", 4);
printTwice(r);
return 0;
}A class with only pure virtual functions (and a virtual destructor) is an interface: a list of what an object can do, with no data and no code. Printable says "this can print itself". A class can inherit from a base class and from interfaces: Report is a Document and is also Printable, Saveable. Code that only needs printing takes a Printable & and works with any class that keeps that promise.
Line by line
Printableis an interface: one pure virtual function,print, and a virtual destructor.Documentis an ordinary base class that holds a title.Reportinherits from both, so it is aDocumentand it isPrintable.Reportoverridesprint, so it is concrete and can be created.printTwicetakes anyconst Printable&, so it works with aReportwithout knowing anything else about it.mainmakes one report and prints it twice through the interface.
Expected result
Report: Lab results (4 pages)
Report: Lab results (4 pages)
In the real world
Java and C# have a separate interface keyword for this idea. In C++, pure virtual classes play the same role in database drivers, audio plug-ins and cross-platform libraries, where each system supplies its own concrete class.
Common mistakes
- Forgetting to override one pure virtual function: the derived class is still abstract, and creating it fails with a message listing what is missing.
- Calling a pure virtual function from the base class's constructor: the derived part does not exist yet.
- Forgetting the virtual destructor in an interface, so deleting through it skips the derived destructor.
Best practices
- Keep interfaces small, with one clear ability each.
- Give abstract classes a virtual destructor.
- Write functions that take the interface, not a concrete class, when that is all they need.
Summary
A pure virtual function makes a class abstract, so it cannot be created, while derived classes that override everything are concrete. A class of pure virtual functions is an interface, and a class may implement several.
Quick check
Key takeaways
- You cannot create an object of an abstract class.
- Pointers and references to an abstract class are allowed.
- An interface describes abilities, and a class can have several.
Practice problems for this lesson
My notes
Write down what you want to remember from this lesson. Notes are saved on this device as you type, and appear on your dashboard.