CodeNebula
C++98corebeginner

Abstract Classes

C++98 · The Foundation · Compiler flag: -std=c++98

Explanation

Classes with pure virtual functions that cannot be instantiated.

Real-World Scenario

A `Vehicle` base class ensuring all vehicles define `move`.

Quick Snippet

snippet.cpp
class Vehicle { virtual void move() = 0; };

Full Example

abstract_classes.cpp
// CodeNebula - C++98 Example: Abstract Classes
// Compile: g++ -std=c++98 -Wall -Wextra -o example abstract_classes.cpp
//
// This example demonstrates:
// - Abstract base classes as design contracts (interfaces)
// - Multiple concrete classes implementing the same abstract interface
// - Factory pattern using abstract base
// - Template Method pattern: base defines algorithm skeleton,
//   derived fills in steps

#include <iostream>
#include <cstring>

// ============================================================
// 1. Abstract Interface: Database
// ============================================================
class Database {
public:
    virtual ~Database() {}

    // Pure virtual interface
    virtual void connect(const char* connectionStr) = 0;
    virtual void disconnect() = 0;
    virtual void execute(const char* query) = 0;
    virtual const char* getName() const = 0;
};

// Concrete: MySQLDatabase
class MySQLDatabase : public Database {
private:
    bool connected;

public:
    MySQLDatabase() : connected(false) {}

    void connect(const char* connectionStr) {
        std::cout << "  [MySQL] Connecting to: " << connectionStr << std::endl;
        connected = true;
    }

    void disconnect() {
        std::cout << "  [MySQL] Disconnected" << std::endl;
        connected = false;
    }

    void execute(const char* query) {
        if (!connected) {
            std::cout << "  [MySQL] ERROR: not connected!" << std::endl;
            return;
        }
        std::cout << "  [MySQL] Executing: " << query << std::endl;
    }

    const char* getName() const { return "MySQL"; }
};

// Concrete: PostgresDatabase
class PostgresDatabase : public Database {
private:
    bool connected;

public:
    PostgresDatabase() : connected(false) {}

    void connect(const char* connectionStr) {
        std::cout << "  [Postgres] Connecting to: " << connectionStr << std::endl;
        connected = true;
    }

    void disconnect() {
        std::cout << "  [Postgres] Disconnected" << std::endl;
        connected = false;
    }

    void execute(const char* query) {
        if (!connected) {
            std::cout << "  [Postgres] ERROR: not connected!" << std::endl;
            return;
        }
        std::cout << "  [Postgres] Executing: " << query << std::endl;
    }

    const char* getName() const { return "PostgreSQL"; }
};

// Function that works with ANY Database -- client code is database-agnostic
void runMigration(Database& db, const char* connStr) {
    std::cout << "  Running migration on " << db.getName() << ":" << std::endl;
    db.connect(connStr);
    db.execute("CREATE TABLE users (id INT, name TEXT)");
    db.execute("INSERT INTO users VALUES (1, 'Alice')");
    db.disconnect();
}

// ============================================================
// 2. Template Method Pattern
//    Base defines the algorithm, derived provides the steps
// ============================================================
class Report {
public:
    virtual ~Report() {}

    // Template method: defines the skeleton (non-virtual, or could be)
    void generate() const {
        std::cout << "  === " << title() << " ===" << std::endl;
        header();
        body();
        footer();
        std::cout << "  === End of Report ===" << std::endl;
    }

protected:
    // Steps to be overridden by derived classes
    virtual const char* title() const = 0;
    virtual void header() const = 0;
    virtual void body() const = 0;

    // Default footer (can be overridden, but not required)
    virtual void footer() const {
        std::cout << "  Footer: Generated by ReportEngine v1.0" << std::endl;
    }
};

class SalesReport : public Report {
protected:
    const char* title() const { return "Sales Report Q4"; }

    void header() const {
        std::cout << "  Date: 2024-12-15  |  Department: Sales" << std::endl;
    }

    void body() const {
        std::cout << "  Total Revenue: $125,000" << std::endl;
        std::cout << "  Units Sold:    500" << std::endl;
        std::cout << "  Top Product:   Widget Pro" << std::endl;
    }
};

class InventoryReport : public Report {
protected:
    const char* title() const { return "Inventory Report"; }

    void header() const {
        std::cout << "  Warehouse: Central  |  Date: 2024-12-15" << std::endl;
    }

    void body() const {
        std::cout << "  Items in stock:  1,234" << std::endl;
        std::cout << "  Low stock items: 12" << std::endl;
    }

    // Override footer with custom version
    void footer() const {
        std::cout << "  Footer: Inventory system auto-generated" << std::endl;
    }
};

// ============================================================
// 3. Simple Factory using abstract base
// ============================================================
Database* createDatabase(const char* type) {
    if (std::strcmp(type, "mysql") == 0) {
        return new MySQLDatabase();
    }
    if (std::strcmp(type, "postgres") == 0) {
        return new PostgresDatabase();
    }
    std::cout << "  Unknown database type: " << type << std::endl;
    return NULL;
}

int main() {
    std::cout << "========================================" << std::endl;
    std::cout << "1. ABSTRACT INTERFACE (Database)" << std::endl;
    std::cout << "========================================" << std::endl;
    {
        // Database db;  // COMPILE ERROR: cannot instantiate abstract class

        MySQLDatabase    mysql;
        PostgresDatabase pg;

        // Same function works with both -- polymorphism via abstract base
        runMigration(mysql, "localhost:3306/mydb");
        std::cout << std::endl;
        runMigration(pg, "localhost:5432/mydb");
    }

    std::cout << "\n========================================" << std::endl;
    std::cout << "2. FACTORY PATTERN WITH ABSTRACT BASE" << std::endl;
    std::cout << "========================================" << std::endl;
    {
        const char* types[] = { "mysql", "postgres", "oracle" };
        const int count = 3;

        for (int i = 0; i < count; ++i) {
            std::cout << "  Creating database: " << types[i] << std::endl;
            Database* db = createDatabase(types[i]);

            if (db != NULL) {
                db->connect("factory-connection");
                db->execute("SELECT 1");
                db->disconnect();
                delete db;
            }
            std::cout << std::endl;
        }
    }

    std::cout << "========================================" << std::endl;
    std::cout << "3. TEMPLATE METHOD PATTERN" << std::endl;
    std::cout << "========================================" << std::endl;
    {
        SalesReport     sales;
        InventoryReport inventory;

        // Same generate() algorithm, different content
        sales.generate();
        std::cout << std::endl;
        inventory.generate();
    }

    std::cout << "\n========================================" << std::endl;
    std::cout << "4. ABSTRACT CLASS AS CONTRACT" << std::endl;
    std::cout << "========================================" << std::endl;
    std::cout << "  An abstract class defines a CONTRACT:" << std::endl;
    std::cout << "  - What methods must every implementation provide?" << std::endl;
    std::cout << "  - Client code depends on the abstract type, not concrete" << std::endl;
    std::cout << "  - New implementations can be added without changing clients" << std::endl;
    std::cout << "  - The abstract class can provide shared default behaviour" << std::endl;
    std::cout << "    (unlike a pure interface with only = 0 methods)" << std::endl;

    return 0;
}

// Expected output:
// ========================================
// 1. ABSTRACT INTERFACE (Database)
// ========================================
//   Running migration on MySQL:
//   [MySQL] Connecting to: localhost:3306/mydb
//   [MySQL] Executing: CREATE TABLE users (id INT, name TEXT)
//   [MySQL] Executing: INSERT INTO users VALUES (1, 'Alice')
//   [MySQL] Disconnected
//
//   Running migration on PostgreSQL:
//   [Postgres] Connecting to: localhost:5432/mydb
//   [Postgres] Executing: CREATE TABLE users (id INT, name TEXT)
//   [Postgres] Executing: INSERT INTO users VALUES (1, 'Alice')
//   [Postgres] Disconnected
//
// ========================================
// 2. FACTORY PATTERN WITH ABSTRACT BASE
// ========================================
//   Creating database: mysql
//   [MySQL] Connecting to: factory-connection
//   [MySQL] Executing: SELECT 1
//   [MySQL] Disconnected
//
//   Creating database: postgres
//   [Postgres] Connecting to: factory-connection
//   [Postgres] Executing: SELECT 1
//   [Postgres] Disconnected
//
//   Creating database: oracle
//   Unknown database type: oracle
//
// ========================================
// 3. TEMPLATE METHOD PATTERN
// ========================================
//   === Sales Report Q4 ===
//   Date: 2024-12-15  |  Department: Sales
//   Total Revenue: $125,000
//   Units Sold:    500
//   Top Product:   Widget Pro
//   Footer: Generated by ReportEngine v1.0
//   === End of Report ===
//
//   === Inventory Report ===
//   Warehouse: Central  |  Date: 2024-12-15
//   Items in stock:  1,234
//   Low stock items: 12
//   Footer: Inventory system auto-generated
//   === End of Report ===
//
// ========================================
// 4. ABSTRACT CLASS AS CONTRACT
// ========================================
//   ...summary...
Run command
g++ -std=c++98 -Wall -Wextra -o example abstract_classes.cpp