Inheritance and Derived Classes
A class that contains a pure virtual function, marked with the =0 suffix, is an abstract class.
A Container That Promises Operations
Imagine a class that describes a container. It stores no data itself. Instead, it lists the operations any container must support, such as reporting its size and adding a value. The interesting question is not what the class does, but which of these operations it promises to provide and which of them it leaves for someone else to provide. This lesson starts from a declaration like that and works outward from it. Keep one question in mind as you read: which member functions does the class promise to provide, and which does it leave unfinished?
What do you think happens?
The Container class declares two operations with no body and no implementation. Can you write a line such as Container c; and create an object from it?
Reveal answer
No, it cannot be created on its own
The restriction does not come from the class being large or from missing data members. It comes from the pure virtual functions the class declares. A class that contains a pure virtual function is abstract, and an abstract class cannot be used to create an object on its own. It serves as an interface for derived classes.
Reading the Container Declaration
The following Container declaration is an illustrative example written for this lesson. It is an interface: it names the operations and their signatures, but it provides no implementation for the two operations that matter most. Read each line and ask whether the function has a body.
class Container {
public:
virtual int size() const = 0;
virtual void push(int value) = 0;
virtual ~Container() {}
};- 1The class has no data members. Its abstractness is not caused by missing data.
- 2Two member functions end in = 0 with no body. These are the pure members.
- 3The destructor has a body, so it is an ordinary virtual function and not a pure one.
- virtual int
size()const = 0; declares a pure virtual function. The virtual keyword allows redefinition in a derived class, and the = 0 suffix makes it pure. - virtual void
push(int value)= 0; is the second pure virtual function. Both operations are requirements placed on future code rather than complete functions. - virtual ~
Container(){} is a virtual destructor with a body. It is virtual but not pure, so it does not make the class abstract.
A pure virtual function is recognized by two things together: the virtual keyword and the = 0 suffix, with no body supplied. Remove either the suffix or the body idea and the function is no longer pure. The ordinary members of the class, such as a destructor with a body, are not pure, even though they are virtual.
Virtual Versus Pure Virtual
A virtual function is a function declared with the word virtual, which means that it may be redefined later in a class derived from this one. A pure virtual function is a virtual function marked with the suffix = 0, which means that some class derived from the interface must define it. A class that contains a pure virtual function is an abstract class.
| Feature | Virtual function with a body | Pure virtual function |
|---|---|---|
| Marked with virtual | Yes | Yes |
| Marked with = 0 | No | Yes |
| Can a derived class redefine it | Yes | Yes |
| Does a derived class have to provide a definition | No | Yes |
| Makes the class abstract | No | Yes |
Both kinds of function can be redefined, but only the pure kind forces a derived class to provide a definition.
Rules That Decide Whether a Class Is Abstract
To decide whether a class is abstract, you ask two questions in order. First, does the class itself declare a pure virtual function? If yes, the class is abstract. Second, if the class declares no new pure virtual function, does it inherit one that it never overrides? If yes, the class is still abstract, because the pure virtual function it inherited has not been given a definition. Only when no pure virtual function remains is the class concrete.
Notice that the second question is the one beginners miss. A derived class can contain no pure keyword at all and still be abstract, because it inherited a pure virtual function that it did not define. The key question is always whether every pure virtual function has been given a definition in the class being examined.
Why the Interface Cannot Become an Object
An interface becomes useful when a class derives from it and supplies the missing functions. The Container declaration sets out what every container must be able to do, but it is not itself a working container. Its pure virtual functions have no bodies, so there is no complete behavior to run when an object is used. This is why the language refuses to build an object from the interface itself. Objects are built only from the derived class, which takes responsibility for making the operations actually work.
Think of the interface as a requirement placed on future code. The base class names the operations, and each derived class that wants to be instantiated must answer that requirement for every pure virtual function it inherited. The interface is never the thing that gets instantiated; the completed derived class is.
Building a Concrete Derived Class
The following derived class is an illustrative example written for this lesson, not taken from the source. It keeps its elements in a plain array and provides a definition for both pure virtual functions. The array has a fixed size and performs no bounds check; this is kept deliberately short so that the focus stays on inheritance.
class ArrayContainer : public Container {
int data[100];
int count = 0;
public:
int size() const override { return count; }
void push(int value) override { data[count] = value; ++count; }
};- 1The derived class declares no new pure virtual functions.
- 2It defines both inherited pure virtual functions, so no pure member remains.
- 3Because nothing pure is left, objects of ArrayContainer can be created, which is not possible for Container.
- class ArrayContainer : public Container names the base class, so this class derives from the interface.
- int
size()const override { return count; } supplies a definition for the first pure virtual function. - void
push(int value)override { ... } supplies a definition for the second pure virtual function.
Each override removes one pure requirement from the list. The class is abstract until the list is empty, and only then can an object be created from it. The derived class must therefore match the interface completely before it can be used as an object.
Tracing a Class That Stays Abstract
Now consider a derived class that defines only one of the two pure virtual functions. This is an illustrative example written for this lesson.
- Broken overrides size but says nothing about push.
- push is still the pure virtual function inherited from Container.
Checking whether Broken can be instantiated
Can an object of type Broken be created?
- 1List the inherited pure virtual functions
Container declares size and push as pure virtual, so Broken inherits both.
- 2List the definitions Broken supplies
Broken overrides size only. It provides no definition for push.
- 3Check what remains pure
push has no definition in Broken, so it is still a pure virtual function, and Broken still contains one.
- 4Decide the class kind
Broken is abstract, so an object of Broken cannot be created on its own.
Broken is abstract. Its missing definition for push is the single problem to fix before it can be instantiated.
When a derived class fails to become usable, ask one question: which pure virtual function from the interface is still missing a definition? In this case the answer is push. Locating that one function usually explains the whole problem, and adding a push override to Broken would make it concrete.
Mistakes Learners Make
Common mistake Deciding a class is abstract because it is large or has no data members
What it looks likeContainer has no data members and only two member functions, yet it is abstract.
Why it failsAbstractness comes from the pure virtual functions, not from the size of the class or from what data it stores.
Do this insteadLook for the = 0 suffix on a virtual function, not for the number of members.
Common mistake Treating a virtual function with a body as pure
What it looks likevirtual ~Container() {} is virtual and has a body.
Why it failsA function with a body is not pure, and a virtual destructor with a body does not make a class abstract.
Do this insteadOnly the = 0 suffix on a virtual function makes it pure.
Common mistake Overriding some pure virtual functions and expecting the class to be instantiable
What it looks likeBroken overrides size but leaves push undefined, and an object of Broken is then attempted.
Why it failsEvery inherited pure virtual function must be defined before the class can be created as an object.
Do this insteadList every inherited pure virtual function and confirm that each one has a definition in the derived class.
Common mistake Trying to create an object from the interface itself
What it looks likeWriting Container c; as if Container were a usable class.
Why it failsThe interface declares operations without implementing them, so it cannot serve as a complete object.
Do this insteadCreate objects only from a derived class that defines all of the pure virtual functions.
Practice: Classify Two Classes
A class Shape declares two members: virtual double area() const = 0; and virtual void draw() const = 0;. A class Circle derives from Shape and overrides only area. Is Circle abstract, and can an object of Circle be created? Explain which function decides the answer. Then say what Circle would need to add for an object to be created.
Hints
- Start by listing every pure virtual function that Circle inherits from Shape.
- Ask which of those functions Circle defines, and which it leaves undefined.
- A class with any remaining pure virtual function is abstract, no matter how many functions it did define.
Write a derived class Square from Shape that defines both area and draw, and state in one sentence whether an object of Square can be created.
Hints
- Both pure virtual functions must have definitions in Square.
- Once no pure virtual function remains, the class is concrete.
Key Takeaways
- A pure virtual function is a virtual function marked with = 0, and a class that contains one is an abstract class.
- An abstract class cannot be used to create an object on its own, because it serves as an interface that derived classes must complete.
- A virtual function with a body, such as a virtual destructor, is not pure and does not make a class abstract.
- A derived class must define every pure virtual function it inherits before objects of that class can be created.
- To predict whether a class is abstract, check whether it declares a pure virtual function or inherits one it never overrides.