Pure Virtual Function with Definition in C++
In C++, declaring a member function as pure virtual typically denotes an abstract interface, requiring derived classes to provide their own implementations. However, the C++ language standard explicitly allows pure virtual functions to contain an actual definition. This article explores how defining a pure virtual function works, where its definition must reside, and the primary design patterns that utilize this capability, such as pure virtual destructors and mandatory overrides with reusable base logic.
Abstract Status Remains Unchanged
Declaring a function with = 0 defines the containing
class as an abstract class. Providing an implementation for that
function does not alter this behavior:
class Base {
public:
virtual void process() = 0;
};
void Base::process() {
// Valid implementation
}Even though Base::process() has a body,
Base cannot be instantiated. Any attempt to create an
object of type Base directly will result in a compilation
error.
Location of the Definition
According to the C++ standard, a pure virtual function cannot define
its body inline inside the class declaration. The pure specifier
(= 0) and the function body cannot share the same
declaration:
// Ill-formed (syntax error)
class Base {
public:
virtual void process() = 0 { }
};
// Well-formed
class Base {
public:
virtual void process() = 0;
};
void Base::process() {
// Implementation outside the class body
}The function body must be placed outside the class declaration,
either in the same header file (often marked inline) or
within a corresponding source (.cpp) file.
Overriding and Explicit Invocation
Derived classes do not inherit the implementation as a default implementation in the virtual dispatch table. To make a derived class instantiable, it must still explicitly override the pure virtual function. If the derived class does not override it, the derived class also remains abstract.
When an override is provided, the derived class can explicitly invoke the base class implementation using qualified name lookup:
class Derived : public Base {
public:
void process() override {
// Explicitly calling the base implementation
Base::process();
// Additional derived-specific logic
}
};This pattern is useful when an interface designer wants to force derived class authors to think about their implementation (preventing accidental inheritance of a default behavior), while still providing reusable core logic that the derived class can opt into calling.
Pure Virtual Destructors
The most critical use case for defining a pure virtual function is the pure virtual destructor. When a designer wants to create an abstract class that has no other natural candidate for a pure virtual function, the destructor can be marked pure virtual:
class AbstractBase {
public:
virtual ~AbstractBase() = 0;
};Unlike regular virtual member functions, base class destructors are
always called implicitly when a derived class object is destroyed. If
AbstractBase does not provide an implementation for its
pure virtual destructor, the code will compile, but the linker will fail
with an undefined reference error as soon as any derived class is
instantiated and destroyed. Therefore, any pure virtual destructor
must have a definition:
AbstractBase::~AbstractBase() {
// Cleanup code, or simply empty
}