Chapter 17 Advanced 52 Questions

Practice Questions — Abstract Classes, Pure Virtual Functions, and Interfaces

← Back to Notes
8 Easy
8 Medium
9 Hard

Topic-Specific Questions

Question 1
Easy
What happens when you try to compile this code?
class Animal {
public:
    virtual void sound() = 0;
    virtual ~Animal() {}
};

int main() {
    Animal a;
    return 0;
}
Animal has a pure virtual function. Can you create an object of it?
Compilation error: cannot declare variable 'a' to be of abstract type 'Animal'.
Question 2
Easy
What is the output?
#include <iostream>
using namespace std;
class Shape {
public:
    virtual double area() const = 0;
    virtual ~Shape() {}
};
class Square : public Shape {
    double side;
public:
    Square(double s) : side(s) {}
    double area() const override { return side * side; }
};
int main() {
    Shape* s = new Square(5);
    cout << s->area() << endl;
    delete s;
    return 0;
}
Square implements area(), so it is concrete. The pointer type is Shape*.
25
Question 3
Easy
Does this code compile?
class Printable {
public:
    virtual void print() const = 0;
    virtual ~Printable() {}
};
class Report : public Printable {
    // print() not implemented
};
int main() {
    Report r;
    return 0;
}
Report does not implement print(). Is it still abstract?
Compilation error: cannot declare variable 'r' to be of abstract type 'Report'.
Question 4
Easy
What is the output?
#include <iostream>
using namespace std;
class Vehicle {
public:
    virtual void start() = 0;
    void honk() { cout << "Beep!" << endl; }
    virtual ~Vehicle() {}
};
class Car : public Vehicle {
public:
    void start() override { cout << "Car started" << endl; }
};
int main() {
    Car c;
    c.start();
    c.honk();
    return 0;
}
honk() is a non-virtual function in the abstract class. Car inherits it.
Car started
Beep!
Question 5
Easy
What is the output?
#include <iostream>
using namespace std;
class IGreeter {
public:
    virtual void greet(const string& name) const = 0;
    virtual ~IGreeter() {}
};
class FormalGreeter : public IGreeter {
public:
    void greet(const string& name) const override {
        cout << "Good morning, " << name << endl;
    }
};
class CasualGreeter : public IGreeter {
public:
    void greet(const string& name) const override {
        cout << "Hey, " << name << "!" << endl;
    }
};
int main() {
    IGreeter* g1 = new FormalGreeter();
    IGreeter* g2 = new CasualGreeter();
    g1->greet("Arjun");
    g2->greet("Kavya");
    delete g1; delete g2;
    return 0;
}
Two concrete classes implementing the same interface, called through interface pointers.
Good morning, Arjun
Hey, Kavya!
Question 6
Medium
What is the output?
#include <iostream>
using namespace std;
class Base {
public:
    virtual void show() = 0;
    virtual ~Base() {}
};
void Base::show() {
    cout << "Base default" << endl;
}
class Derived : public Base {
public:
    void show() override {
        Base::show();
        cout << "Derived extra" << endl;
    }
};
int main() {
    Derived d;
    d.show();
    return 0;
}
A pure virtual function can have a definition. Derived calls it with Base::show().
Base default
Derived extra
Question 7
Medium
What is the output?
#include <iostream>
using namespace std;
class AbstractBase {
public:
    virtual ~AbstractBase() = 0;
    void work() { cout << "Working" << endl; }
};
AbstractBase::~AbstractBase() { cout << "~AbstractBase" << endl; }

class Concrete : public AbstractBase {
public:
    ~Concrete() override { cout << "~Concrete" << endl; }
};
int main() {
    AbstractBase* p = new Concrete();
    p->work();
    delete p;
    return 0;
}
Pure virtual destructor makes the class abstract. It still needs a definition.
Working
~Concrete
~AbstractBase
Question 8
Medium
What is the output?
#include <iostream>
using namespace std;
class IPrint {
public:
    virtual void print() const = 0;
    virtual ~IPrint() {}
};
class ISave {
public:
    virtual void save() const = 0;
    virtual ~ISave() {}
};
class Doc : public IPrint, public ISave {
    string content;
public:
    Doc(string c) : content(c) {}
    void print() const override { cout << "Print: " << content << endl; }
    void save() const override { cout << "Save: " << content << endl; }
};
int main() {
    Doc d("Hello World");
    IPrint* p = &d;
    ISave* s = &d;
    p->print();
    s->save();
    return 0;
}
Doc implements two interfaces. Each interface pointer can access only its own methods.
Print: Hello World
Save: Hello World
Question 9
Medium
What is the output?
#include <iostream>
using namespace std;
class Animal {
public:
    virtual void sound() const = 0;
    virtual void move() const = 0;
    virtual ~Animal() {}
};
class Pet : public Animal {
public:
    void move() const override { cout << "Walks at home" << endl; }
};
class Dog : public Pet {
public:
    void sound() const override { cout << "Bark" << endl; }
};
int main() {
    Dog d;
    d.sound();
    d.move();
    return 0;
}
Pet implements move() but not sound(). Dog implements sound(). Together, all pure virtuals are covered.
Bark
Walks at home
Question 10
Hard
What is the output?
#include <iostream>
using namespace std;
class A {
public:
    virtual void f() = 0;
    virtual ~A() {}
};
void A::f() { cout << "A::f" << endl; }

class B : public A {
public:
    void f() override { cout << "B::f" << endl; }
};
class C : public A {
public:
    void f() override {
        A::f();
        cout << "C::f" << endl;
    }
};
int main() {
    A* arr[] = { new B(), new C() };
    for (int i = 0; i < 2; i++) {
        arr[i]->f();
        cout << "---" << endl;
        delete arr[i];
    }
    return 0;
}
B ignores A::f(). C calls A::f() explicitly before adding its own output.
B::f
---
A::f
C::f
---
Question 11
Hard
Does this code compile? If so, what is the output?
#include <iostream>
using namespace std;
class Shape {
public:
    virtual double area() const = 0;
    virtual ~Shape() {}
};
class Circle : public Shape {
    double r;
public:
    Circle(double r) : r(r) {}
    double area() const override { return 3.14 * r * r; }
};
void printArea(Shape s) {
    cout << s.area() << endl;
}
int main() {
    Circle c(5);
    printArea(c);
    return 0;
}
printArea takes Shape by value. Can you copy an abstract type?
Compilation error: cannot declare parameter 's' to be of abstract type 'Shape'.
Question 12
Hard
What is the output?
#include <iostream>
using namespace std;
class IEngine {
public:
    virtual void start() = 0;
    virtual ~IEngine() {}
};
class IElectric {
public:
    virtual int battery() const = 0;
    virtual ~IElectric() {}
};
class HybridCar : public IEngine, public IElectric {
public:
    void start() override { cout << "Hybrid started" << endl; }
    int battery() const override { return 85; }
};
int main() {
    HybridCar h;
    IEngine* e = &h;
    IElectric* el = &h;
    e->start();
    cout << "Battery: " << el->battery() << "%" << endl;
    return 0;
}
HybridCar implements two interfaces. Each pointer accesses only its interface's methods.
Hybrid started
Battery: 85%
Question 13
Hard
What is the output?
#include <iostream>
using namespace std;
class Base {
public:
    virtual void f() { cout << "Base::f" << endl; }
    virtual void g() = 0;
    virtual ~Base() {}
};
class Mid : public Base {
public:
    void g() override { cout << "Mid::g" << endl; }
};
class Leaf : public Mid {
public:
    void f() override { cout << "Leaf::f" << endl; }
};
int main() {
    Base* b = new Leaf();
    b->f();
    b->g();
    delete b;
    return 0;
}
f() is virtual (not pure) in Base, overridden in Leaf. g() is pure virtual in Base, implemented in Mid, inherited by Leaf.
Leaf::f
Mid::g
Question 14
Hard
What is the output?
#include <iostream>
using namespace std;
class Shape {
public:
    virtual void draw() const = 0;
    virtual ~Shape() { cout << "~Shape" << endl; }
};
class Circle : public Shape {
public:
    void draw() const override { cout << "Circle" << endl; }
    ~Circle() { cout << "~Circle" << endl; }
};
class ColoredCircle : public Circle {
    string color;
public:
    ColoredCircle(string c) : color(c) {}
    void draw() const override { cout << color << " Circle" << endl; }
    ~ColoredCircle() { cout << "~ColoredCircle" << endl; }
};
int main() {
    Shape* s = new ColoredCircle("Red");
    s->draw();
    delete s;
    return 0;
}
Three-level hierarchy. Virtual destructor. All three destructors should run.
Red Circle
~ColoredCircle
~Circle
~Shape

Mixed & Application Questions

Question 1
Easy
What makes a class abstract in C++?
Think about pure virtual functions.
A class is abstract if it has at least one pure virtual function (declared with = 0). An abstract class cannot be instantiated directly. Only concrete derived classes that implement all pure virtual functions can be instantiated.
Question 2
Easy
How do you create an interface in C++?
C++ does not have an interface keyword.
In C++, an interface is created by writing a class with only pure virtual functions and a virtual destructor, with no data members. By convention, interface names are often prefixed with 'I' (e.g., IDrawable, ISerializable).
Question 3
Easy
What is the output?
#include <iostream>
using namespace std;
class Calculator {
public:
    virtual int compute(int a, int b) const = 0;
    virtual ~Calculator() {}
};
class Adder : public Calculator {
public:
    int compute(int a, int b) const override { return a + b; }
};
class Multiplier : public Calculator {
public:
    int compute(int a, int b) const override { return a * b; }
};
int main() {
    Calculator* c1 = new Adder();
    Calculator* c2 = new Multiplier();
    cout << c1->compute(3, 4) << endl;
    cout << c2->compute(3, 4) << endl;
    delete c1; delete c2;
    return 0;
}
Two concrete classes implementing the same abstract interface.
7
12
Question 4
Medium
What is the output?
#include <iostream>
using namespace std;
class A {
public:
    virtual void f() = 0;
    void g() { cout << "A::g calls f: "; f(); }
    virtual ~A() {}
};
class B : public A {
public:
    void f() override { cout << "B::f" << endl; }
};
int main() {
    B b;
    b.g();
    return 0;
}
g() is non-virtual in A. It calls f() which is virtual. Which f() runs?
A::g calls f: B::f
Question 5
Medium
Can a pure virtual function have a definition in C++? If so, how is it called?
Think about providing a default implementation that derived classes can optionally use.
Yes, a pure virtual function can have a definition (body) provided outside the class. The class is still abstract and cannot be instantiated. Derived classes can call the base definition using Base::func(). This allows providing optional default behavior while still forcing derived classes to override.
Question 6
Medium
What is the output?
#include <iostream>
using namespace std;
class Shape {
public:
    virtual double area() const = 0;
    virtual ~Shape() {}
    bool isLargerThan(const Shape& other) const {
        return area() > other.area();
    }
};
class Circle : public Shape {
    double r;
public:
    Circle(double r) : r(r) {}
    double area() const override { return 3.14 * r * r; }
};
class Square : public Shape {
    double s;
public:
    Square(double s) : s(s) {}
    double area() const override { return s * s; }
};
int main() {
    Circle c(5);
    Square sq(8);
    cout << c.isLargerThan(sq) << endl;
    cout << sq.isLargerThan(c) << endl;
    return 0;
}
isLargerThan is non-virtual and calls the virtual area() on both this and other.
1
0
Question 7
Medium
What is the abstract destructor pattern and when would you use it?
Think about a class where all methods have implementations but you still want it to be abstract.
The abstract destructor pattern declares the destructor as pure virtual (virtual ~Base() = 0) while providing a definition outside the class. This makes the class abstract even when all other methods have implementations. You use it when you want to prevent direct instantiation of a base class that has no pure virtual behavior methods.
Question 8
Hard
What is the output?
#include <iostream>
using namespace std;
class ILog {
public:
    virtual void log(const string& msg) const = 0;
    virtual ~ILog() {}
};
class INotify {
public:
    virtual void notify(const string& msg) const = 0;
    virtual ~INotify() {}
};
class Service : public ILog, public INotify {
public:
    void log(const string& msg) const override {
        cout << "[LOG] " << msg << endl;
    }
    void notify(const string& msg) const override {
        cout << "[NOTIFY] " << msg << endl;
    }
};
void doLog(const ILog& logger) { logger.log("System started"); }
void doNotify(const INotify& notifier) { notifier.notify("Alert!"); }
int main() {
    Service s;
    doLog(s);
    doNotify(s);
    return 0;
}
Service implements two interfaces. Each function accepts a different interface reference.
[LOG] System started
[NOTIFY] Alert!
Question 9
Hard
What is the output?
#include <iostream>
using namespace std;
class Base {
public:
    virtual void action() = 0;
    virtual ~Base() = 0;
};
Base::~Base() { cout << "~Base" << endl; }
void Base::action() { cout << "Base::action" << endl; }

class Mid : public Base {
public:
    void action() override {
        Base::action();
        cout << "Mid::action" << endl;
    }
    ~Mid() override { cout << "~Mid" << endl; }
};
class Leaf : public Mid {
public:
    void action() override {
        Mid::action();
        cout << "Leaf::action" << endl;
    }
    ~Leaf() override { cout << "~Leaf" << endl; }
};
int main() {
    Base* p = new Leaf();
    p->action();
    delete p;
    return 0;
}
Each level calls the parent's action() before adding its own. The destructor chain runs in reverse.
Base::action
Mid::action
Leaf::action
~Leaf
~Mid
~Base
Question 10
Hard
Does this code compile?
#include <iostream>
using namespace std;
class IComparable {
public:
    virtual bool lessThan(const IComparable& other) const = 0;
    virtual ~IComparable() {}
};
class Score : public IComparable {
    int val;
public:
    Score(int v) : val(v) {}
    bool lessThan(const IComparable& other) const override {
        const Score& s = dynamic_cast<const Score&>(other);
        return val < s.val;
    }
};
int main() {
    Score a(85), b(92);
    cout << a.lessThan(b) << endl;
    cout << b.lessThan(a) << endl;
    return 0;
}
dynamic_cast on a reference throws bad_cast on failure. Here both are Score objects.
1
0
Question 11
Hard
Why is multiple inheritance of interfaces safer than multiple inheritance of concrete classes?
Think about data members and the diamond problem.
Interfaces (classes with only pure virtual functions) have no data members and no implementation. Multiple inheritance of interfaces cannot cause data duplication, ambiguous member access, or the diamond problem's data ambiguity. With concrete classes, multiple inheritance can lead to duplicate base class subobjects, ambiguous function calls, and the need for virtual inheritance.

Multiple Choice Questions

MCQ 1
What makes a class abstract in C++?
  • A. Using the abstract keyword
  • B. Having at least one pure virtual function
  • C. Having a private constructor
  • D. Having no data members
Answer: B
B is correct. C++ does not have an abstract keyword. A class is abstract if it has at least one pure virtual function (declared with = 0).
MCQ 2
How is a pure virtual function declared?
  • A. virtual void func() = 0;
  • B. pure virtual void func();
  • C. abstract void func();
  • D. void func() = delete;
Answer: A
A is correct. A pure virtual function is declared with = 0 at the end. = delete is a different feature that explicitly deletes a function.
MCQ 3
Can you create an object of an abstract class?
  • A. Yes, always
  • B. No, never
  • C. Yes, if it has a constructor
  • D. Yes, if it has data members
Answer: B
B is correct. Abstract classes cannot be instantiated. You can have pointers and references to an abstract class type, but the actual object must be a concrete derived class.
MCQ 4
What is an interface in C++?
  • A. A class declared with the interface keyword
  • B. A class with only pure virtual functions and a virtual destructor
  • C. Any class with virtual functions
  • D. A struct with public members
Answer: B
B is correct. C++ has no interface keyword. By convention, an interface is a class with only pure virtual functions (and typically a virtual destructor), with no data members or implementations.
MCQ 5
What happens if a derived class does not implement all pure virtual functions?
  • A. Runtime error
  • B. The missing functions return default values
  • C. The derived class is also abstract
  • D. The compiler uses the base class version
Answer: C
C is correct. If a derived class does not implement all pure virtual functions from its base, it inherits the remaining pure virtual functions and is itself abstract. It cannot be instantiated.
MCQ 6
Can a pure virtual function have a definition (body)?
  • A. No, that defeats the purpose
  • B. Yes, defined outside the class; derived classes call it with Base::func()
  • C. Yes, but only inside the class body
  • D. No, it causes a compilation error
Answer: B
B is correct. A pure virtual function can have a definition provided outside the class body. The class is still abstract. Derived classes can call it with Base::func() to use the default behavior.
MCQ 7
What is required when using a pure virtual destructor?
  • A. Nothing special
  • B. A definition must be provided outside the class
  • C. The derived class must not have a destructor
  • D. The class must have no other virtual functions
Answer: B
B is correct. A pure virtual destructor must have a definition provided outside the class because destructors are always called in the inheritance chain. Without the definition, you get a linker error.
MCQ 8
Why is multiple inheritance of interfaces generally safe?
  • A. Because interfaces use virtual inheritance by default
  • B. Because interfaces have no data members, avoiding diamond problem ambiguity
  • C. Because C++ prevents multiple interface inheritance
  • D. Because interfaces are templates
Answer: B
B is correct. The diamond problem's ambiguity comes from duplicate data members. Interfaces have no data members and no implementation, so inheriting from multiple interfaces cannot cause data duplication or ambiguous member access.
MCQ 9
Which of the following can an abstract class have?
  • A. Only pure virtual functions
  • B. Both pure virtual and non-virtual functions, plus data members
  • C. Only static functions
  • D. Only a destructor
Answer: B
B is correct. An abstract class can have pure virtual functions, regular virtual functions, non-virtual functions, data members, constructors, and destructors. It just needs at least one pure virtual function to be abstract.
MCQ 10
Can you have a pointer or reference to an abstract class?
  • A. No, abstract classes cannot be used as types
  • B. Only pointers, not references
  • C. Yes, both pointers and references are allowed
  • D. Only references, not pointers
Answer: C
C is correct. You can have Shape* s or Shape& s where Shape is abstract. The actual object must be concrete. This is the foundation of runtime polymorphism.
MCQ 11
What is the output?
class A { public: virtual void f() = 0; virtual ~A(){} };
void A::f() { cout << "A"; }
class B : public A { public: void f() override { A::f(); cout << "B"; } };
int main() { B b; b.f(); }
  • A. A
  • B. AB
  • C. B
  • D. Compilation error
Answer: B
B is correct. B::f() calls A::f() which prints "A", then prints "B". The result is "AB". A pure virtual function with a definition can be called explicitly.
MCQ 12
Which statement about abstract classes is FALSE?
  • A. An abstract class can have constructors
  • B. An abstract class can have data members
  • C. You can create arrays of abstract class objects
  • D. You can have arrays of pointers to abstract class
Answer: C
C is correct (it is the false statement). You cannot create arrays of abstract class objects because that would require instantiating the abstract class. You CAN have arrays of pointers (Shape* arr[10]) or references to abstract types.
MCQ 13
When should you use an abstract class instead of an interface?
  • A. When you want multiple inheritance
  • B. When you need shared data members or partial implementation among derived classes
  • C. When you want compile-time polymorphism
  • D. When you want to use templates
Answer: B
B is correct. Use an abstract class when derived classes share common data or implementation. Use an interface when you only want to define a contract with no shared state or implementation.

Coding Challenges

Challenge 1: Shape Hierarchy with Abstract Base

Easy
Create an abstract Shape class with pure virtual area() and perimeter(). Implement Circle, Rectangle, and Triangle. Store pointers in a Shape* array. Print each shape's name, area, and perimeter.
Sample Input
Circle r=5, Rectangle 4x6, Triangle 3-4-5
Sample Output
Circle: area=78.54, perimeter=31.42 Rectangle: area=24.00, perimeter=20.00 Triangle: area=6.00, perimeter=12.00
Use pure virtual functions. Use override. Include virtual destructor.
#include <iostream>
#include <cmath>
using namespace std;

class Shape {
public:
    virtual double area() const = 0;
    virtual double perimeter() const = 0;
    virtual string name() const = 0;
    virtual ~Shape() {}
};

class Circle : public Shape {
    double r;
public:
    Circle(double r) : r(r) {}
    double area() const override { return M_PI * r * r; }
    double perimeter() const override { return 2 * M_PI * r; }
    string name() const override { return "Circle"; }
};

class Rectangle : public Shape {
    double w, h;
public:
    Rectangle(double w, double h) : w(w), h(h) {}
    double area() const override { return w * h; }
    double perimeter() const override { return 2 * (w + h); }
    string name() const override { return "Rectangle"; }
};

class Triangle : public Shape {
    double a, b, c;
public:
    Triangle(double a, double b, double c) : a(a), b(b), c(c) {}
    double area() const override {
        double s = (a + b + c) / 2;
        return sqrt(s * (s-a) * (s-b) * (s-c));
    }
    double perimeter() const override { return a + b + c; }
    string name() const override { return "Triangle"; }
};

int main() {
    Shape* shapes[] = { new Circle(5), new Rectangle(4, 6), new Triangle(3, 4, 5) };
    for (int i = 0; i < 3; i++) {
        printf("%s: area=%.2f, perimeter=%.2f\n",
               shapes[i]->name().c_str(), shapes[i]->area(), shapes[i]->perimeter());
        delete shapes[i];
    }
    return 0;
}

Challenge 2: Multiple Interface Implementation

Medium
Create interfaces IReadable (with read()), IWritable (with write(string)), and IClosable (with close()). Create a File class that implements all three. Create a process() function that takes an IReadable& and reads, and a save() function that takes an IWritable& and writes.
Sample Input
File operations
Sample Output
Reading from data.txt Writing 'Hello World' to data.txt Closing data.txt
Each interface should have only pure virtual functions and virtual destructor. Use references.
#include <iostream>
#include <string>
using namespace std;

class IReadable {
public:
    virtual string read() const = 0;
    virtual ~IReadable() {}
};

class IWritable {
public:
    virtual void write(const string& data) = 0;
    virtual ~IWritable() {}
};

class IClosable {
public:
    virtual void close() = 0;
    virtual ~IClosable() {}
};

class File : public IReadable, public IWritable, public IClosable {
    string filename;
public:
    File(string name) : filename(name) {}
    string read() const override {
        cout << "Reading from " << filename << endl;
        return "file contents";
    }
    void write(const string& data) override {
        cout << "Writing '" << data << "' to " << filename << endl;
    }
    void close() override {
        cout << "Closing " << filename << endl;
    }
};

void process(const IReadable& r) { r.read(); }
void save(IWritable& w, const string& data) { w.write(data); }

int main() {
    File f("data.txt");
    process(f);
    save(f, "Hello World");
    f.close();
    return 0;
}

Challenge 3: Database Abstraction Layer

Hard
Create an abstract Database class with pure virtual connect(), query(string), and disconnect(). Add a non-virtual executeWithLog(string) that logs and calls query(). Implement MySQL and PostgreSQL classes. Process an array of Database* pointers.
Sample Input
Two databases, each executing a query
Sample Output
[LOG] SELECT * FROM users MySQL: Executing 'SELECT * FROM users' [LOG] SELECT * FROM users PostgreSQL: Executing 'SELECT * FROM users'
Use abstract class with mixed pure virtual and non-virtual. Demonstrate that non-virtual methods call virtual methods polymorphically.
#include <iostream>
#include <string>
using namespace std;

class Database {
public:
    virtual void connect() = 0;
    virtual string query(const string& sql) = 0;
    virtual void disconnect() = 0;
    virtual ~Database() {}

    void executeWithLog(const string& sql) {
        cout << "[LOG] " << sql << endl;
        query(sql);
    }
};

class MySQL : public Database {
public:
    void connect() override { cout << "MySQL connected" << endl; }
    string query(const string& sql) override {
        cout << "MySQL: Executing '" << sql << "'" << endl;
        return "mysql result";
    }
    void disconnect() override { cout << "MySQL disconnected" << endl; }
};

class PostgreSQL : public Database {
public:
    void connect() override { cout << "PostgreSQL connected" << endl; }
    string query(const string& sql) override {
        cout << "PostgreSQL: Executing '" << sql << "'" << endl;
        return "pg result";
    }
    void disconnect() override { cout << "PostgreSQL disconnected" << endl; }
};

int main() {
    Database* dbs[] = { new MySQL(), new PostgreSQL() };
    for (int i = 0; i < 2; i++) {
        dbs[i]->connect();
        dbs[i]->executeWithLog("SELECT * FROM users");
        dbs[i]->disconnect();
        cout << "---" << endl;
        delete dbs[i];
    }
    return 0;
}

Challenge 4: Plugin System with Interface

Hard
Create an IPlugin interface with name(), version(), initialize(), execute(string), and shutdown(). Create LogPlugin and CachePlugin implementations. Create a PluginManager that stores IPlugin* pointers and has loadAll(), runAll(string command), and unloadAll() methods.
Sample Input
Run all plugins with command 'process-data'
Sample Output
Loading LogPlugin v1.0... Loading CachePlugin v2.1... LogPlugin: Logging 'process-data' CachePlugin: Caching 'process-data' Unloading CachePlugin... Unloading LogPlugin...
Interface must have only pure virtual functions. Manager must work with IPlugin* only.
#include <iostream>
#include <vector>
#include <string>
using namespace std;

class IPlugin {
public:
    virtual string name() const = 0;
    virtual string version() const = 0;
    virtual void initialize() = 0;
    virtual void execute(const string& cmd) = 0;
    virtual void shutdown() = 0;
    virtual ~IPlugin() {}
};

class LogPlugin : public IPlugin {
public:
    string name() const override { return "LogPlugin"; }
    string version() const override { return "1.0"; }
    void initialize() override { cout << "Loading " << name() << " v" << version() << "..." << endl; }
    void execute(const string& cmd) override { cout << name() << ": Logging '" << cmd << "'" << endl; }
    void shutdown() override { cout << "Unloading " << name() << "..." << endl; }
};

class CachePlugin : public IPlugin {
public:
    string name() const override { return "CachePlugin"; }
    string version() const override { return "2.1"; }
    void initialize() override { cout << "Loading " << name() << " v" << version() << "..." << endl; }
    void execute(const string& cmd) override { cout << name() << ": Caching '" << cmd << "'" << endl; }
    void shutdown() override { cout << "Unloading " << name() << "..." << endl; }
};

class PluginManager {
    vector<IPlugin*> plugins;
public:
    void addPlugin(IPlugin* p) { plugins.push_back(p); }
    void loadAll() { for (auto* p : plugins) p->initialize(); }
    void runAll(const string& cmd) { for (auto* p : plugins) p->execute(cmd); }
    void unloadAll() {
        for (int i = plugins.size()-1; i >= 0; i--) plugins[i]->shutdown();
    }
    ~PluginManager() { for (auto* p : plugins) delete p; }
};

int main() {
    PluginManager mgr;
    mgr.addPlugin(new LogPlugin());
    mgr.addPlugin(new CachePlugin());
    mgr.loadAll();
    mgr.runAll("process-data");
    mgr.unloadAll();
    return 0;
}

Need to Review the Concepts?

Go back to the detailed notes for this chapter.

Read Chapter Notes

Want to learn C++ with a live mentor?

Explore our C++ Masterclass