C++ OOP
Abstraction
Complete lesson for Abstraction using LEARNCODEGURU’s 6-level learning model — from basics to advanced practice, with C++ OOP and STL where relevant.
Basics — Abstraction
Section: C++ OOP. C++ abstraction — hide implementation details, show essential features with abstract classes and pure virtual functions.
What is abstraction?
- **Abstraction** means showing only the **necessary information** and **hiding internal details**.
- It reduces complexity — focus on **what** an object does, not **how** it does it.
- In C++, abstraction is mainly achieved with **abstract classes** and **pure virtual functions**.
- Users (client code) call a clear interface; the messy internals stay hidden.
- **Real-life:** you press a TV remote’s Power button — you do not wire the circuits yourself.
- **Real-life:** car ignition — you turn the key; fuel injection and spark timing stay hidden.
| Idea | Meaning | In code |
|---|---|---|
| Show essentials | Public contract / behaviour | draw(), start() |
| Hide details | Internal algorithm / data | private fields, method bodies |
| Abstract base | Blueprint for derived types | virtual void f() = 0; |
| Concrete derived | Real implementation | Circle, Car override pure virtuals |
Abstraction — hide details, show essentials
Abstraction focuses on what an object does, not how. The diagram covers abstract classes, pure virtual functions, the Shape example, Vehicle analogy, rules, benefits, and use cases.
- Show: essential features via a clear interface.
- Hide: implementation details inside derived classes.
- Tools: abstract classes + pure virtual functions (
= 0). - Rule: cannot instantiate abstract types; override all pure virtuals to go concrete.
Why use abstraction?
- Hides unnecessary details from callers.
- Reduces complexity of large systems.
- Improves security and safety — internals are not casually reachable.
- Enhances maintainability — swap implementation without changing the API.
- Supports reusability and extensibility — new derived types plug into the same interface.
| Benefit | What you gain | Example |
|---|---|---|
| Less noise | Callers see only essentials | shape->draw() |
| Lower complexity | Think in interfaces | Payment gateway API |
| Safety | Hide sensitive logic | Auth internals private |
| Maintainability | Change how without changing what | New Circle algorithm |
| Extensibility | Add types without rewriting callers | New Triangle : Shape |
| Reuse | One base contract, many impls | Vehicle → Car / Bike |
Abstraction in C++
- An **abstract class** contains **at least one pure virtual function**.
- A **pure virtual** is declared with
= 0and has no required body in the base. - You **cannot create objects** of an abstract class — it is a blueprint.
- Abstract classes provide an **interface** for derived classes to implement.
| Feature | Description |
|---|---|
| Abstract class | Class with ≥ 1 pure virtual function |
| Pure virtual function | Declared virtual … = 0; — no body required in base |
| Cannot create object | Shape s; is a compile error if Shape is abstract |
| Used for | Interface / blueprint for derived classes |
| May also have | Data members, constructors, non-pure methods |
How abstraction works
- Define an **abstract base** with pure virtual methods (the interface).
- **Derived classes** implement those methods differently.
- Client code often uses a **base pointer or reference** and calls the common interface.
- Same call site (
ptr->draw()) can reach Circle or Rectangle behaviour (runtime polymorphism).
// Abstract interface
class Shape {
public:
virtual void draw() = 0;
virtual void area() = 0;
void info() { /* shared non-pure method OK */ }
virtual ~Shape() = default;
};
// Derived implementations
class Circle : public Shape { /* override draw, area */ };
class Rectangle : public Shape { /* override draw, area */ };
// Client uses the interface
Shape* ptr = &circle;
ptr->draw(); // essential feature — details hidden Pure virtual functions
- Syntax:
virtual ReturnType name(params) = 0; - Makes the class abstract (if at least one pure virtual exists).
- Forces every concrete derived class to provide an override (unless it stays abstract).
- Optional: you can still give a pure virtual a body later for shared default logic, but derived must still override to be concrete.
class Animal {
public:
virtual void sound() = 0; // pure virtual
virtual ~Animal() = default;
};
class Dog : public Animal {
public:
void sound() override { /* bark */ }
}; Abstract class rules
- Must have **at least one** pure virtual function.
- **Cannot** create objects of an abstract class.
- **Can** have data members, constructors, and ordinary (non-pure) member functions.
- Used as a **base class** — derive concrete types from it.
- If a derived class leaves any pure virtual unimplemented, **it stays abstract** too.
- Prefer a **virtual destructor** in abstract bases used polymorphically.
| Rule | Allowed? | Note |
|---|---|---|
| Object of abstract class | No | Shape s; → error |
| Pointer / reference to abstract | Yes | Shape* p = &circle; |
| Non-pure methods in abstract | Yes | Shared helpers like info() |
| Data members in abstract | Yes | Keep them protected/private carefully |
| Partial override in derived | Derived stays abstract | Must override all pure virtuals |
Example — Shape / Circle / Rectangle
- Classic demo: abstract
Shapewithdraw()andarea(); Circle and Rectangle implement both. - Through abstraction, the **same interface** works for different objects.
#include <iostream>
using namespace std;
class Shape {
public:
virtual void draw() = 0;
virtual void area() = 0;
void info() { cout << "This is a shape.\n"; }
virtual ~Shape() = default;
};
class Circle : public Shape {
float r;
public:
Circle(float radius) : r(radius) {}
void draw() override { cout << "Drawing Circle\n"; }
void area() override {
cout << "Area of Circle: " << 3.14f * r * r << "\n";
}
};
class Rectangle : public Shape {
float l, b;
public:
Rectangle(float len, float bre) : l(len), b(bre) {}
void draw() override { cout << "Drawing Rectangle\n"; }
void area() override {
cout << "Area of Rectangle: " << l * b << "\n";
}
};
int main() {
Shape* ptr;
Circle c(5.0f);
Rectangle r(4.0f, 3.0f);
ptr = &c;
ptr->info(); ptr->draw(); ptr->area();
cout << "\n";
ptr = &r;
ptr->info(); ptr->draw(); ptr->area();
return 0;
} Real-life analogy — Vehicle blueprint
- **Vehicle** as abstract blueprint: every vehicle must
start()andstop()— *what* it does. - **Car / Bike / Bus** supply their own implementations — *how* each does it.
- Callers care about start/stop, not engine internals.
class Vehicle {
public:
virtual void start() = 0;
virtual void stop() = 0;
virtual ~Vehicle() = default;
};
class Car : public Vehicle {
public:
void start() override { /* ignition, fuel inject */ }
void stop() override { /* brake, engine off */ }
};
class Bike : public Vehicle {
public:
void start() override { /* kick / electric start */ }
void stop() override { /* clutch, engine off */ }
}; Abstraction vs encapsulation
- Both hide details, but at different layers.
- **Encapsulation:** wrap data + methods; control access (
private/ getters). - **Abstraction:** design the **interface** — show essentials, hide implementation strategy.
- Often used together: abstract public API + encapsulated private data.
| Aspect | Abstraction | Encapsulation |
|---|---|---|
| Focus | What to show / hide at design level | How data is protected inside a class |
| Main tools | Abstract class, pure virtual | Access specifiers, getters/setters |
| Question | What does the object do? | Who may touch the data? |
| Example | Shape::draw() interface | private balance + deposit() |
Key points to remember
- Abstraction hides implementation and shows only what is essential.
- Achieved using abstract classes and pure virtual functions.
- Abstract classes cannot be instantiated.
- Derived classes must implement all pure virtuals to become concrete.
- Supports runtime polymorphism via base pointers/references.
- Common in frameworks, interfaces, plug-and-play plugins, libraries, and security layers.
Advanced — Abstraction
Interface-style abstract classes
- C++ has no
interfacekeyword — a class with **only pure virtuals** (+ virtual dtor) acts like one. - Keep interface classes focused: small set of operations, no heavy data.
- Prefer composition of interfaces over giant “god” abstract bases.
class IPrinter {
public:
virtual void print(const char* text) = 0;
virtual ~IPrinter() = default;
};
class LaserPrinter : public IPrinter {
public:
void print(const char* text) override { /* laser path */ }
}; Partial abstraction
- Abstract classes may mix pure virtuals with concrete helpers.
- Shared non-pure methods reduce duplication in derived classes.
- Still cannot instantiate the abstract base.
class Account {
protected:
double balance = 0;
public:
virtual void withdraw(double x) = 0; // must customize
double getBalance() const { return balance; } // shared
virtual ~Account() = default;
}; Virtual destructor in abstract bases
- If you delete a derived object through a base pointer, the base destructor should be
virtual. - Abstract bases used polymorphically almost always need
virtual ~Base() = default;(or override). - Without it: undefined behaviour / resource leaks on
delete basePtr;.
class Shape {
public:
virtual void draw() = 0;
virtual ~Shape() = default; // required for polymorphic delete
}; Cannot instantiate — what you can do
Shape s;fails if Shape is abstract.- You **can** use
Shape*/Shape&bound to concrete derived objects. - You **can** use containers of
Shape*or smart pointers to Shape.
// Shape s; // ERROR
Circle c(5);
Shape* p = &c; // OK
Shape& r = c; // OK
p->draw(); Design tips & use cases
- Name abstract types by role:
Shape,Vehicle,ISerializable. - Keep pure virtual APIs stable — changing them breaks all implementers.
- **Use cases:** framework design, interface design, plug-and-play systems, security layers, library APIs.
- Do not force abstraction everywhere — simple concrete classes are fine when there is only one implementation.
Abstraction and polymorphism together
- Pure virtuals + overrides enable **runtime** polymorphism.
- Client loops over
Shape*and callsdraw()— each concrete type responds differently. - Deeper vtable / override details belong with Virtual Functions / Polymorphism lessons.
Tricky Interview Points
Q1) What is abstraction in C++?
Answer Showing only essential features to the user while hiding implementation details — typically via abstract classes and pure virtual functions.
Q2) How is abstraction achieved in C++?
Answer Mainly with abstract classes (classes that have at least one pure virtual function) that define an interface for derived classes.
Q3) What is a pure virtual function?
Answer A virtual function declared with = 0. It has no required body in the base and makes the class abstract.
Q4) What is an abstract class?
Answer A class with one or more pure virtual functions. It cannot be instantiated and serves as a blueprint for derived classes.
Q5) Can we create an object of an abstract class?
Answer No. You may use pointers or references to the abstract type that point/refer to concrete derived objects.
Q6) Must derived classes implement all pure virtual functions?
Answer Yes, to become concrete. If any pure virtual remains unimplemented, the derived class stays abstract.
Q7) Can an abstract class have non-pure member functions?
Answer Yes. It can have data members, constructors, and ordinary methods alongside pure virtuals.
Q8) Abstraction vs encapsulation?
Answer Abstraction focuses on hiding complexity behind an interface (what). Encapsulation binds data with methods and restricts access (how data is protected).
Q9) Why use a virtual destructor in an abstract base?
Answer So deleting a derived object through a base pointer calls the correct derived destructor and avoids undefined behaviour.
Q10) Does C++ have an interface keyword?
Answer No. An abstract class with only pure virtual functions (plus usually a virtual destructor) is used as an interface.
Q11) Give a real-life example of abstraction.
Answer Vehicle as abstract blueprint with start()/stop(); Car, Bike, Bus each implement those operations differently.
Q12) Where is abstraction used in real projects?
Answer Frameworks, plugin systems, library APIs, device drivers’ public interfaces, payment gateways, and security modules.
Simple Programs
Abstract Shape with Circle and Rectangle
#include <iostream>
using namespace std;
class Shape {
public:
virtual void draw() = 0;
virtual void area() = 0;
void info() { cout << "This is a shape.\n"; }
virtual ~Shape() = default;
};
class Circle : public Shape {
float r;
public:
Circle(float radius) : r(radius) {}
void draw() override { cout << "Drawing Circle\n"; }
void area() override {
cout << "Area of Circle: " << 3.14f * r * r << "\n";
}
};
class Rectangle : public Shape {
float l, b;
public:
Rectangle(float len, float bre) : l(len), b(bre) {}
void draw() override { cout << "Drawing Rectangle\n"; }
void area() override {
cout << "Area of Rectangle: " << l * b << "\n";
}
};
int main() {
Shape* ptr;
Circle c(5.0f);
Rectangle r(4.0f, 3.0f);
ptr = &c;
ptr->info(); ptr->draw(); ptr->area();
cout << "\n";
ptr = &r;
ptr->info(); ptr->draw(); ptr->area();
return 0;
} Vehicle abstract blueprint
#include <iostream>
using namespace std;
class Vehicle {
public:
virtual void start() = 0;
virtual void stop() = 0;
virtual ~Vehicle() = default;
};
class Car : public Vehicle {
public:
void start() override { cout << "Car starts\n"; }
void stop() override { cout << "Car stops\n"; }
};
class Bike : public Vehicle {
public:
void start() override { cout << "Bike starts\n"; }
void stop() override { cout << "Bike stops\n"; }
};
int main() {
Vehicle* v;
Car c; Bike b;
v = &c; v->start(); v->stop();
v = &b; v->start(); v->stop();
return 0;
} Still abstract if override incomplete
#include <iostream>
using namespace std;
class Animal {
public:
virtual void eat() = 0;
virtual void sound() = 0;
virtual ~Animal() = default;
};
class Dog : public Animal {
public:
void eat() override { cout << "Dog eats\n"; }
// sound() not overridden → Dog is still abstract
};
class Labrador : public Dog {
public:
void sound() override { cout << "Bark\n"; }
};
int main() {
// Dog d; // ERROR — still abstract
Labrador lab;
lab.eat();
lab.sound();
return 0;
} Interface-style printer
#include <iostream>
#include <string>
using namespace std;
class IPrinter {
public:
virtual void print(const string& text) = 0;
virtual ~IPrinter() = default;
};
class ConsolePrinter : public IPrinter {
public:
void print(const string& text) override {
cout << "[console] " << text << "\n";
}
};
void report(IPrinter& out) {
out.print("Report ready");
}
int main() {
ConsolePrinter p;
report(p);
return 0;
} Array of abstract pointers
#include <iostream>
using namespace std;
class Shape {
public:
virtual void draw() const = 0;
virtual ~Shape() = default;
};
class Circle : public Shape {
public:
void draw() const override { cout << "Circle\n"; }
};
class Square : public Shape {
public:
void draw() const override { cout << "Square\n"; }
};
int main() {
Circle c;
Square s;
Shape* shapes[2] = { &c, &s };
for (Shape* p : shapes) p->draw();
return 0;
} Partial abstraction with shared method
#include <iostream>
using namespace std;
class Account {
protected:
double balance;
public:
Account(double b) : balance(b) {}
virtual void withdraw(double x) = 0;
void show() const { cout << "Balance: " << balance << "\n"; }
virtual ~Account() = default;
};
class Savings : public Account {
public:
Savings(double b) : Account(b) {}
void withdraw(double x) override {
if (x > 0 && x <= balance) balance -= x;
}
};
int main() {
Savings a(1000);
a.withdraw(200);
a.show();
return 0;
} Problem Solving Tricks
- Abstraction = show what, hide how.
- Pure virtual: virtual void f() = 0;
- Any pure virtual → class is abstract.
- Cannot create abstract objects — use pointers/references.
- Override ALL pure virtuals to make a concrete class.
- Abstract classes can still have normal methods and data.
- Always give polymorphic bases a virtual destructor.
- C++ “interface” ≈ abstract class of pure virtuals.
- Abstraction ≠ encapsulation — use both.
- Prefer small, stable interfaces over huge abstract bases.
- Same Shape* interface → many concrete behaviours.
- If only one implementation forever, a concrete class may be enough.
Practice Programs List
- Build abstract Shape with draw()/area(); add Triangle as a third derived class.
- Model Vehicle with start()/stop(); implement Car, Bike, and Bus.
- Create ILogger with log(string); implement ConsoleLogger and FileLogger stubs.
- Show that Shape s; fails to compile; Shape* p = &circle; works.
- Leave one pure virtual unimplemented in a mid-level class; prove it stays abstract.
- Add a non-pure info() on the abstract base and call it via base pointer.
- Store several Shape* in an array and call draw() in a loop.
- Write a short note: abstraction vs encapsulation with one code sketch each.
- Add virtual ~Base() and explain why polymorphic delete needs it.
- Design a tiny payment interface: pay(amount); implement UpiPay and CardPay.
- Refactor duplicated “print header” into a non-pure method on an abstract Report.
- List five real apps where you only see an interface (ATM, remote, OS APIs, …).