Virtual Functions
A class that contains at least one pure virtual function, marked with the = 0 suffix, is an abstract class.
Reading a Container Interface
Imagine a class that describes a container. It stores no data of its own. Instead, it lists the operations any container must support. The question to keep in mind while reading is simple: which of these member functions does the class promise to provide, and which does it leave for some other class to provide?
- push and pop are declared with = 0, so both are pure virtual functions
- The destructor is virtual but has a body, so it is not pure
- The class declares operations and their signatures but gives no implementation for push or pop
The declaration is an interface. It names the operations and their signatures, and it provides no implementation for the two operations that matter most. Whatever class eventually supplies those implementations is responsible for making the container actually work. The rest of this article explains what that responsibility means for the compiler and for you.
What do you think happens?
Could a program create an object of type Container directly, as in Container c;?
Reveal answer
No, because the class contains pure virtual functions
The restriction does not come from the class being large or from missing data members. It comes from the pure virtual functions marked with = 0.
Virtual Versus Pure Virtual
Two separate ideas are combined in each declaration of an interface. The first is the word virtual. In this context, virtual means that a function may be redefined later in a class derived from this one. A function declared virtual is called a virtual function.
A virtual function is a member function declared with the keyword virtual, which means a class derived from this one may redefine it.
The second idea is the suffix = 0. This suffix marks the function as pure virtual. A pure virtual function is one that some class derived from the interface must define. The declaration is therefore a requirement placed on future code rather than a complete function. The source treats this suffix as the mechanism that turns a class into an interface.
A pure virtual function is a virtual function declared with the suffix = 0. A class derived from the interface must define it.
A class that contains at least one pure virtual function, marked with the = 0 suffix, is an abstract class.
Reading the =0 Suffix
When you read a declaration, look at each member function and ask whether it ends in = 0 before the semicolon. The anatomy below takes one pure virtual declaration apart token by token.
The = 0 token is the only thing that separates a pure virtual function from an ordinary virtual function. A virtual declaration with a body, such as the virtual destructor in the Container example, has no = 0 suffix. It may be redefined in a derived class, but nothing forces a derived class to define it, so it does not make the class abstract.
Why an Abstract Class Cannot Be Created
An abstract class cannot be used to create an object on its own. It serves as an interface for derived classes. The Container interface declares what must exist, but it does not supply those operations, so it is not used to build objects. Only a derived class that supplies every missing operation is used to build objects.
The diagram shows the relationship the code expresses. The interface sits at the top and declares what must exist. The derived class sits below and supplies those operations. The interface itself is not used to build objects; only the derived class is. The restriction on creating objects therefore lives in the interface, and the object-creating power lives in the derived class.
Tracing Which Functions Are Pure
To decide whether a class is abstract, scan every member function. Mark each one as pure, virtual with a body, or ordinary. Then ask a single question: does any function carry the = 0 suffix? The worked example below follows that trace.
Classifying the Members of a Sensor Class
class Sensor {
public:
virtual void read() = 0;
virtual void reset() = 0;
virtual void describe() {}
virtual ~Sensor() {}
};Is Sensor abstract, and which members are responsible?
- 1List the members
There are four virtual member functions: read, reset, describe, and the destructor.
- 2Check each for = 0
read ends in = 0, so it is pure. reset also ends in = 0, so it is pure. describe has a body and no = 0, so it is not pure. The destructor has a body and no = 0, so it is not pure.
- 3Apply the rule
A class that contains a pure virtual function is abstract. Sensor contains two pure virtual functions, so it is abstract.
- 4Conclude about objects
Sensor cannot be used to create an object on its own. A derived class must define both read and reset before it can be created.
Sensor is abstract. The pure members are read and reset. describe and the virtual destructor are virtual but not pure, so they do not cause the restriction.
Completing a Derived Class
An interface becomes useful when a class derives from it and supplies the missing functions. The class below keeps its elements in a plain array and implements both pure virtual functions from the Container interface. It is an illustrative example written for this article.
- The class header names Container as its base, so it inherits both pure virtual functions
- push is defined here, so it is no longer pure in this class
- pop is defined here, so it is no longer pure in this class
- Because no pure virtual function remains undefined, ArrayContainer can be created as an object
The key question in every case is whether all pure virtual functions have been given definitions in the class being created. A derived class that leaves one of them undefined still contains a pure virtual function, so it remains abstract. This is why the derived class has to match the interface completely before it can be used as an object.
When a derived class cannot be instantiated, ask one question first: which pure virtual function from the interface is still missing a definition? Locating that single function usually explains the whole problem.
| Feature | Virtual function | Pure virtual function |
|---|---|---|
| Declaration form | virtual void f(); | virtual void f() = 0; |
| Can a derived class redefine it? | Yes | Yes |
| Must a derived class define it? | No | Yes, before objects can be created |
| Makes the class abstract? | No | Yes |
Both kinds are open to redefinition, but only the pure kind forces a derived class to provide a definition.
Mistakes Beginners Make with Pure Virtuals
Common mistake Deciding a class is abstract because it is large or has no data members
What it looks likeclass Logger { public: virtual void log(const char* msg) = 0; };Why it failsThe restriction does not come from size or data members. It comes from the = 0 suffix.
Do this insteadScan the member functions for = 0. The Logger above is abstract because of log, not because it has no data.
Common mistake Treating a virtual function with a body as pure
What it looks likevirtual ~Container() {}Why it failsThis declaration is virtual, but it has no = 0 suffix and provides a body, so it is not pure.
Do this insteadA virtual function with a body does not make the class abstract. Only a pure virtual function does.
Common mistake Leaving one pure virtual function undefined in a derived class
Why it failsThe derived class still contains a pure virtual function, so it remains abstract and cannot be created.
Do this insteadCompare the derived class against every pure virtual function in the interface and define each missing one.
Common mistake Trying to create an object of the interface class itself, such as Container c; when Container still has pure virtual functions
Practice Classifying Classes
Consider this class: class Widget { public: virtual void draw() = 0; virtual void resize(int w) {} virtual ~Widget() {} }; Is Widget abstract? Name the member responsible, and state what a derived class must do before it can be created as an object.
Hints
- Look for the = 0 suffix on each member function.
- resize and the destructor have bodies and no = 0, so they are not pure.
- A derived class must define every pure virtual function it inherits, which here means draw.
Key Takeaways
- A pure virtual function is a virtual function marked with the = 0 suffix, and a class that contains one is an abstract class.
- An abstract class cannot be used to create an object on its own; it serves as an interface for derived classes.
- A derived class must define every pure virtual function it inherits before it can be created as an object. A virtual function with a body, such as a virtual destructor, is not pure and does not make a class abstract.
- To predict whether a class is abstract, scan every member function for the = 0 suffix and check whether any is missing a definition in the derived class.