C++98corebeginner
Pure Virtual Functions
C++98 · The Foundation · Compiler flag: -std=c++98
Explanation
Virtual functions declared with `= 0`, making a class abstract.
Real-World Scenario
Define a `Shape` base class requiring all shapes to implement `draw`.
Quick Snippet
snippet.cpp
class Shape { virtual void draw() = 0; };Full Example
pure_virtual_functions.cpp
// CodeNebula - C++98 Example: Pure Virtual Functions
// Compile: g++ -std=c++98 -Wall -Wextra -o example pure_virtual_functions.cpp
//
// This example demonstrates:
// - Declaring pure virtual functions with = 0
// - Classes with pure virtual functions become abstract (cannot be instantiated)
// - Derived classes MUST override all pure virtuals to be instantiable
// - Using abstract classes as interfaces
// - A pure virtual function CAN have a body (optional default implementation)
#include <iostream>
#include <cstring>
// ============================================================
// 1. Abstract base class: Serializable (interface-like)
// ============================================================
class Serializable {
public:
virtual ~Serializable() {}
// Pure virtual: every Serializable object must implement these
virtual void serialize() const = 0;
virtual void deserialize(const char* data) = 0;
};
// ============================================================
// 2. Abstract base class: Logger
// Has a pure virtual with a body (default implementation)
// ============================================================
class Logger {
public:
virtual ~Logger() {}
// Pure virtual WITH a body -- derived classes must still override,
// but can call Logger::log() as a default implementation.
virtual void log(const char* message) const = 0;
void logInfo(const char* msg) const {
std::cout << " [INFO] ";
log(msg); // virtual dispatch
}
};
// Providing a body for the pure virtual function
// (defined outside the class, as required by the standard)
void Logger::log(const char* message) const {
std::cout << "[DefaultLogger] " << message << std::endl;
}
// ============================================================
// 3. Abstract base class: Animal (some pure, some concrete)
// ============================================================
class Animal {
protected:
char name[64];
public:
Animal(const char* n) {
std::strncpy(name, n, 63);
name[63] = '\0';
}
virtual ~Animal() {}
// Pure virtual -- MUST be overridden
virtual void speak() const = 0;
virtual void move() const = 0;
// Concrete (non-pure) -- can be inherited as-is
void identify() const {
std::cout << " I am " << name << std::endl;
}
};
// ============================================================
// Concrete derived classes
// ============================================================
class Dog : public Animal {
public:
Dog(const char* n) : Animal(n) {}
void speak() const {
std::cout << " " << name << " says: Woof!" << std::endl;
}
void move() const {
std::cout << " " << name << " runs on four legs" << std::endl;
}
};
class Bird : public Animal {
public:
Bird(const char* n) : Animal(n) {}
void speak() const {
std::cout << " " << name << " says: Tweet!" << std::endl;
}
void move() const {
std::cout << " " << name << " flies through the air" << std::endl;
}
};
// PartialAnimal: only overrides speak(), NOT move() -- still abstract!
class PartialAnimal : public Animal {
public:
PartialAnimal(const char* n) : Animal(n) {}
void speak() const {
std::cout << " " << name << " says: ???" << std::endl;
}
// move() is NOT overridden -- PartialAnimal is still abstract
};
// CompleteAnimal: derives from PartialAnimal and provides move()
class CompleteAnimal : public PartialAnimal {
public:
CompleteAnimal(const char* n) : PartialAnimal(n) {}
void move() const {
std::cout << " " << name << " walks slowly" << std::endl;
}
};
// ============================================================
// Concrete: ConsoleLogger (implements Logger)
// ============================================================
class ConsoleLogger : public Logger {
public:
void log(const char* message) const {
std::cout << "[Console] " << message << std::endl;
}
};
// FileLogger: uses the base class's default implementation
class FileLogger : public Logger {
public:
void log(const char* message) const {
// Use the default implementation from Logger
std::cout << " (FileLogger calling Logger::log) ";
Logger::log(message);
}
};
// ============================================================
// Concrete: Config (implements Serializable)
// ============================================================
class Config : public Serializable {
private:
int volume;
int brightness;
public:
Config(int v, int b) : volume(v), brightness(b) {}
void serialize() const {
std::cout << " Serialized: vol=" << volume
<< ",bright=" << brightness << std::endl;
}
void deserialize(const char* data) {
std::cout << " Deserializing from: \"" << data << "\"" << std::endl;
// Simplified parsing for demonstration
volume = 50;
brightness = 75;
std::cout << " Result: vol=" << volume
<< ", bright=" << brightness << std::endl;
}
};
int main() {
std::cout << "========================================" << std::endl;
std::cout << "1. CANNOT INSTANTIATE ABSTRACT CLASS" << std::endl;
std::cout << "========================================" << std::endl;
{
// Animal a("test"); // ERROR: cannot instantiate abstract class
// Logger l; // ERROR: same
// PartialAnimal p("p"); // ERROR: move() is still pure virtual
std::cout << " Animal a(\"test\"); // COMPILE ERROR" << std::endl;
std::cout << " Logger l; // COMPILE ERROR" << std::endl;
std::cout << " PartialAnimal p(\"p\"); // COMPILE ERROR (move() still pure)" << std::endl;
std::cout << std::endl;
std::cout << " CompleteAnimal c(\"c\"); // OK -- all pure virtuals overridden" << std::endl;
}
std::cout << "\n========================================" << std::endl;
std::cout << "2. POLYMORPHISM WITH PURE VIRTUAL FUNCTIONS" << std::endl;
std::cout << "========================================" << std::endl;
{
Dog d("Rex");
Bird b("Tweety");
// Array of base pointers
const int COUNT = 2;
Animal* animals[COUNT];
animals[0] = &d;
animals[1] = &b;
for (int i = 0; i < COUNT; ++i) {
animals[i]->identify();
animals[i]->speak(); // pure virtual -- dispatches to derived
animals[i]->move(); // pure virtual -- dispatches to derived
std::cout << std::endl;
}
}
std::cout << "========================================" << std::endl;
std::cout << "3. GRADUAL OVERRIDE (PartialAnimal chain)" << std::endl;
std::cout << "========================================" << std::endl;
{
CompleteAnimal ca("Turtle");
ca.identify();
ca.speak(); // from PartialAnimal
ca.move(); // from CompleteAnimal
}
std::cout << "\n========================================" << std::endl;
std::cout << "4. PURE VIRTUAL WITH DEFAULT BODY" << std::endl;
std::cout << "========================================" << std::endl;
{
ConsoleLogger cl;
FileLogger fl;
std::cout << " ConsoleLogger:" << std::endl;
cl.logInfo("Application started");
std::cout << "\n FileLogger (uses Logger::log default):" << std::endl;
fl.logInfo("Writing to file");
}
std::cout << "\n========================================" << std::endl;
std::cout << "5. INTERFACE PATTERN (Serializable)" << std::endl;
std::cout << "========================================" << std::endl;
{
Config cfg(80, 60);
Serializable* s = &cfg; // base pointer
s->serialize();
s->deserialize("vol=50,bright=75");
}
std::cout << "\n========================================" << std::endl;
std::cout << "6. SUMMARY" << std::endl;
std::cout << "========================================" << std::endl;
std::cout << " - Pure virtual: virtual void f() = 0;" << std::endl;
std::cout << " - Makes the class abstract (cannot instantiate)" << std::endl;
std::cout << " - Derived MUST override ALL pure virtuals to be concrete" << std::endl;
std::cout << " - A pure virtual CAN have a body (optional default)" << std::endl;
std::cout << " - Partial override chains: still abstract until all are overridden" << std::endl;
return 0;
}
// Expected output:
// ========================================
// 1. CANNOT INSTANTIATE ABSTRACT CLASS
// ========================================
// Animal a("test"); // COMPILE ERROR
// Logger l; // COMPILE ERROR
// PartialAnimal p("p"); // COMPILE ERROR (move() still pure)
//
// CompleteAnimal c("c"); // OK -- all pure virtuals overridden
//
// ========================================
// 2. POLYMORPHISM WITH PURE VIRTUAL FUNCTIONS
// ========================================
// I am Rex
// Rex says: Woof!
// Rex runs on four legs
//
// I am Tweety
// Tweety says: Tweet!
// Tweety flies through the air
//
// ========================================
// 3. GRADUAL OVERRIDE (PartialAnimal chain)
// ========================================
// I am Turtle
// Turtle says: ???
// Turtle walks slowly
//
// ========================================
// 4. PURE VIRTUAL WITH DEFAULT BODY
// ========================================
// ConsoleLogger:
// [INFO] [Console] Application started
//
// FileLogger (uses Logger::log default):
// [INFO] (FileLogger calling Logger::log) [DefaultLogger] Writing to file
//
// ========================================
// 5. INTERFACE PATTERN (Serializable)
// ========================================
// Serialized: vol=80,bright=60
// Deserializing from: "vol=50,bright=75"
// Result: vol=50, bright=75
//
// ========================================
// 6. SUMMARY
// ========================================
// ...summary text...
Run command
g++ -std=c++98 -Wall -Wextra -o example pure_virtual_functions.cpp