READMEC++

README

Advanced OOP / Abstract Classes

Concept Lesson
Intermediate
4 min

Learning Objective

Understand Advanced OOP well enough to explain it, recognize it in C++, and apply it in a small task.

Why It Matters

This concept is part of the foundation that later lessons and projects assume you already understand.

OopTable Of ContentsWhat Are Abstract ClassesPurposePure Virtual Functions
Private notes
0/8000

Notes stay private to your browser until account sync is configured.

README
1 min read15 headings

Abstract Classes in C++

Table of Contents

  1. What are Abstract Classes
  2. Pure Virtual Functions
  3. Interface Pattern
  4. Abstract vs Concrete
  5. Partial Implementation
  6. Best Practices

What are Abstract Classes

An abstract class cannot be instantiated - it serves as a base for other classes.

class Shape {
public:
    virtual double area() = 0;  // Pure virtual = abstract
};

// Shape s;  // ERROR: cannot instantiate

Purpose

  • Define interfaces
  • Enforce derived class contracts
  • Provide common base functionality

Pure Virtual Functions

A function with = 0 is pure virtual:

class Animal {
public:
    virtual void speak() = 0;    // Must be overridden
    virtual void move() = 0;     // Must be overridden

    void breathe() {             // Regular method - inherited
        cout << "Breathing" << endl;
    }

    virtual ~Animal() = default;
};

class Dog : public Animal {
public:
    void speak() override { cout << "Woof!" << endl; }
    void move() override { cout << "Running" << endl; }
};

Rules

  • Class with any pure virtual = abstract
  • Derived class must override ALL pure virtuals to be concrete
  • Pure virtuals CAN have implementation (see below)

Interface Pattern

Pure interfaces have ONLY pure virtual functions:

class IDrawable {
public:
    virtual void draw() = 0;
    virtual ~IDrawable() = default;
};

class IClickable {
public:
    virtual void onClick() = 0;
    virtual ~IClickable() = default;
};

class Button : public IDrawable, public IClickable {
public:
    void draw() override { cout << "[Button]" << endl; }
    void onClick() override { cout << "Clicked!" << endl; }
};

Naming Convention

  • Prefix with I (IDrawable, ISerializable)
  • Or suffix with able (Drawable, Serializable)

Abstract vs Concrete

Abstract ClassConcrete Class
Has ≥1 pure virtualNo pure virtuals
Cannot instantiateCan instantiate
Serves as interface/baseFully implemented
// Abstract - has pure virtual
class Vehicle {
public:
    virtual void drive() = 0;
};

// Still abstract - didn't implement all
class Car : public Vehicle {
    // drive() still pure virtual
};

// Concrete - all implemented
class Sedan : public Car {
public:
    void drive() override { cout << "Driving sedan" << endl; }
};

Sedan s;  // OK
// Car c;    // ERROR
// Vehicle v; // ERROR

Partial Implementation

Abstract classes can provide default implementations:

class Logger {
public:
    virtual void log(const string& msg) = 0;

    // Default implementations
    void info(const string& msg) { log("[INFO] " + msg); }
    void error(const string& msg) { log("[ERROR] " + msg); }

    virtual ~Logger() = default;
};

class ConsoleLogger : public Logger {
public:
    void log(const string& msg) override {
        cout << msg << endl;
    }
};

ConsoleLogger logger;
logger.info("Started");   // Uses inherited info()
logger.error("Failed");   // Uses inherited error()

Pure Virtual WITH Body

class Base {
public:
    virtual void method() = 0;  // Still abstract!
};

// Provide default implementation
void Base::method() {
    cout << "Default implementation" << endl;
}

class Derived : public Base {
public:
    void method() override {
        Base::method();  // Can call default
        cout << "Extended" << endl;
    }
};

Best Practices

✅ Do

// 1. Virtual destructor
class Interface {
public:
    virtual void method() = 0;
    virtual ~Interface() = default;
};

// 2. Use for polymorphism
void process(Shape& s) { s.draw(); }

// 3. Keep interfaces small
class IReader { virtual void read() = 0; };
class IWriter { virtual void write() = 0; };

❌ Don't

// 1. Don't add data members to pure interfaces
class IBad {
    int data;  // Avoid in pure interfaces
    virtual void method() = 0;
};

// 2. Don't forget to implement all pure virtuals
class Incomplete : public Animal {
    void speak() override {}
    // Forgot move() - still abstract!
};

Quick Reference

// Abstract class
class Abstract {
public:
    virtual void pureMethod() = 0;     // Must override
    virtual void withDefault() {}       // Can override
    void concrete() {}                  // Regular method
    virtual ~Abstract() = default;
};

// Pure interface
class IInterface {
public:
    virtual void method1() = 0;
    virtual void method2() = 0;
    virtual ~IInterface() = default;
};

// Concrete implementation
class Concrete : public Abstract, public IInterface {
public:
    void pureMethod() override {}
    void method1() override {}
    void method2() override {}
};

Compile & Run

g++ -std=c++17 -Wall examples.cpp -o examples && ./examples

Skill Check

Test this lesson

Answer 4 quick questions to lock in the lesson and feed your adaptive practice queue.

--
Score
0/4
Answered
Not attempted
Status
1

Which module does this lesson belong to?

2

Which section is covered in this lesson content?

3

Which term is most central to this lesson?

4

What is the best way to use this lesson for real learning?

Your answers save locally first, then sync when account storage is available.
Practice queue