Abstract Class (Pure Virtual Functions)
A class that has at least one pure virtual function (= 0) is called an abstract class and cannot be instantiated directly. Abstract classes are used to define interfaces and common design contracts. A class where all members are pure virtual functions is the C++ equivalent of an interface in Java or C#.
Syntax
// Declares a pure virtual function with = 0.
// A class with at least one pure virtual function becomes an abstract class.
class AbstractBase {
public:
virtual void pureMethod() = 0; // pure virtual function (no implementation)
virtual void normalMethod() {
// regular virtual function (can have an implementation)
}
virtual ~AbstractBase() {} // virtual destructor is also required
};
// AbstractBase obj; // compile error (abstract class cannot be instantiated)
// Overriding all pure virtual functions in a derived class creates a concrete class.
class ConcreteClass : public AbstractBase {
public:
void pureMethod() override {
// concrete implementation goes here
}
};
ConcreteClass obj; // OK (all pure virtual functions are implemented)
// Interface pattern: a class where all members are pure virtual functions
class IAttackable {
public:
virtual void attack() = 0;
virtual void defend() = 0;
virtual ~IAttackable() {}
};
Syntax Reference
| Syntax / Concept | Description |
|---|---|
| virtual void f() = 0 | Declares a pure virtual function. It has no implementation and forces derived classes to override it. |
| Abstract class | A class with at least one pure virtual function. It cannot be instantiated and must be used through derived classes. |
| Concrete class | A class that inherits from an abstract class and overrides all pure virtual functions. It can be instantiated. |
| Interface design | A pattern that uses a class whose all members are pure virtual functions as an interface. Combined with multiple inheritance, a single class can implement multiple interfaces. |
Sample Code
eva_abstract.cpp
#include <iostream>
#include <string>
#include <vector>
// Interface: IAttackable
// A pure interface representing the ability to attack and defend.
class IAttackable {
public:
virtual void attack() = 0;
virtual void defend() = 0;
virtual ~IAttackable() {}
};
// Abstract class: Angel (common base for Angels)
// Inherits IAttackable and adds the pure virtual function describe().
class Angel : public IAttackable {
protected:
std::string name;
int coreHP;
public:
Angel(const std::string& name, int coreHP)
: name(name), coreHP(coreHP) {}
virtual void describe() = 0;
void showStatus() {
std::cout << "[" << name << "] Core HP: " << coreHP << std::endl;
}
virtual ~Angel() {}
};
// Concrete class: Sachiel (3rd Angel)
class Sachiel : public Angel {
public:
Sachiel() : Angel("Sachiel (3rd Angel)", 3000) {}
void describe() override {
std::cout << name << ": A giant humanoid Angel with two horns and an exposed skeleton." << std::endl;
}
void attack() override {
std::cout << name << " fires a proton cannon!" << std::endl;
}
void defend() override {
std::cout << name << " deploys an A.T. Field." << std::endl;
}
};
// Concrete class: Ramiel (5th Angel)
class Ramiel : public Angel {
public:
Ramiel() : Angel("Ramiel (5th Angel)", 8000) {}
void describe() override {
std::cout << name << ": A geometrically perfect octahedron Angel." << std::endl;
}
void attack() override {
std::cout << name << " fires a charged particle beam!" << std::endl;
}
void defend() override {
std::cout << name << " covers all sides with an ultra-hard A.T. Field." << std::endl;
}
};
// Concrete class: Bardiel (8th Angel)
class Bardiel : public Angel {
public:
Bardiel() : Angel("Bardiel (8th Angel)", 5500) {}
void describe() override {
std::cout << name << ": A parasitic Angel that infects and takes over other units." << std::endl;
}
void attack() override {
std::cout << name << " uses infected tentacles to attack!" << std::endl;
}
void defend() override {
std::cout << name << " uses the host body as a shield." << std::endl;
}
};
void simulateBattle(Angel* angel) {
angel->showStatus();
angel->describe();
angel->defend();
angel->attack();
std::cout << std::endl;
}
int main() {
std::vector<Angel*> angels;
angels.push_back(new Sachiel());
angels.push_back(new Ramiel());
angels.push_back(new Bardiel());
std::cout << "=== Angel Interception Simulation ===" << std::endl << std::endl;
for (int i = 0; i < (int)angels.size(); i++) {
simulateBattle(angels[i]);
}
// Can also be used as an IAttackable pointer
std::cout << "=== Call via interface pointer ===" << std::endl;
IAttackable* iface = angels[0];
iface->attack();
std::cout << std::endl;
for (int i = 0; i < (int)angels.size(); i++) {
delete angels[i];
}
return 0;
}
g++ -std=c++11 eva_abstract.cpp -o eva_abstract ./eva_abstract === Angel Interception Simulation === [Sachiel (3rd Angel)] Core HP: 3000 Sachiel (3rd Angel): A giant humanoid Angel with two horns and an exposed skeleton. Sachiel (3rd Angel) deploys an A.T. Field. Sachiel (3rd Angel) fires a proton cannon! [Ramiel (5th Angel)] Core HP: 8000 Ramiel (5th Angel): A geometrically perfect octahedron Angel. Ramiel (5th Angel) covers all sides with an ultra-hard A.T. Field. Ramiel (5th Angel) fires a charged particle beam! [Bardiel (8th Angel)] Core HP: 5500 Bardiel (8th Angel): A parasitic Angel that infects and takes over other units. Bardiel (8th Angel) uses the host body as a shield. Bardiel (8th Angel) uses infected tentacles to attack! === Call via interface pointer === Sachiel (3rd Angel) fires a proton cannon!
Common Mistake 1: Forgetting to override all pure virtual functions
If a derived class does not override all pure virtual functions from the abstract base class, the derived class also becomes abstract and cannot be instantiated.
// NG: derived class missing the describe() override
class IncompleteAngel : public Angel {
public:
void attack() override { std::cout << "attack" << std::endl; }
void defend() override { std::cout << "defend" << std::endl; }
// describe() is not overridden -> IncompleteAngel is still abstract
};
OK: Override all pure virtual functions from the base class.
// OK: all pure virtual functions are overridden
class CompleteAngel : public Angel {
public:
CompleteAngel() : Angel("Test Angel", 1000) {}
void describe() override { std::cout << "complete implementation" << std::endl; }
void attack() override { std::cout << "attack" << std::endl; }
void defend() override { std::cout << "defend" << std::endl; }
};
Common Mistake 2: Missing virtual destructor
Without a virtual destructor in the abstract base class, the derived class destructor is not called when deleting through a base class pointer, causing resource leaks.
// NG: no virtual destructor
class BadBase {
public:
virtual void pureMethod() = 0;
// ~BadBase() is not virtual -> derived destructor is not called on delete
};
OK: Define a virtual destructor.
// OK: define a virtual destructor
class GoodBase {
public:
virtual void pureMethod() = 0;
virtual ~GoodBase() {} // ensures derived destructor is called correctly
};
Overview
A class with at least one pure virtual function (virtual void f() = 0) is an abstract class. Attempting to instantiate it directly results in a compile error. Abstract classes enforce a design contract that derived classes must provide concrete implementations for all pure virtual functions. When all pure virtual functions are overridden, the derived class becomes a concrete class that can be instantiated. The pattern of making all members pure virtual functions is widely used in C++ as an interface pattern, and multiple interfaces can be implemented in a single class using multiple inheritance. When working with abstract class pointers, always define a virtual destructor to ensure the correct destructor is called when deleting through a base class pointer.
If you find any errors or copyright issues, please contact us.