Lesson 52 of 60 – Abstraction in C++
87%

Abstraction in C++

Abstraction is an important concept of Object-Oriented Programming (OOP). It means hiding unnecessary implementation details and showing only the essential features of an object or system.

Note: In C++, abstraction can be implemented using classes, access specifiers, abstract classes, and pure virtual functions.

1. What is Abstraction?

Abstraction means showing only the important information and hiding unnecessary implementation details.

For example, when you use an ATM, you select options such as withdrawal or balance enquiry. You do not need to know the internal implementation of the ATM software.

class ATM {

public:

    void withdraw() {

        // Complex internal process
        std::cout <<
            "Money withdrawn";
    }
};

2. Meaning of Abstraction

The main idea of abstraction is:

  • Show essential features.
  • Hide unnecessary implementation details.
  • Provide a simple interface.
  • Reduce complexity for the user of a class.

Abstraction allows programmers to focus on what an object does instead of how it does it.

3. Real-World Example of Abstraction

Consider a car.

  • You use the steering wheel to control direction.
  • You use the accelerator to increase speed.
  • You use the brake to stop the car.

You do not need to understand every internal engine operation to drive the car.

This is a simple real-world example of abstraction.

4. Abstraction in C++

In C++, abstraction can be achieved using classes and access specifiers.

class BankAccount {

private:

    double balance;

public:

    void deposit(double amount) {

        balance += amount;
    }
};

The user can call deposit() without directly accessing the internal balance variable.

5. Abstraction Using Private Members

Private members can hide internal data and implementation details from code outside the class.

class Student {

private:

    int marks;

public:

    void setMarks(int m) {

        if(m >= 0 && m <= 100) {

            marks = m;
        }
    }

    int getMarks() {

        return marks;
    }
};

The internal variable marks is hidden from direct external access.

6. Public Interface

The functions or members that are available to users of a class form part of its public interface.

class Calculator {

private:

    int result;

public:

    void add(int a, int b) {

        result = a + b;
    }

    int getResult() {

        return result;
    }
};

Users interact with the class through its public functions rather than directly modifying its internal data.

7. Hiding Implementation Details

A class can hide complex operations behind a simple public function.

class EmailService {

private:

    void connectToServer() {

        // Complex implementation
    }

    void prepareMessage() {

        // Complex implementation
    }

public:

    void sendEmail() {

        connectToServer();

        prepareMessage();

        std::cout <<
            "Email sent";
    }
};

The user only needs to call sendEmail().

8. Abstract Classes

An abstract class is a class that contains at least one pure virtual function.

class Shape {

public:

    virtual double area() = 0;
};

The class provides an interface but does not provide an implementation for area().

9. Pure Virtual Function

A pure virtual function is declared by assigning 0 to a virtual function.

class Shape {

public:

    virtual void draw() = 0;
};

The derived class must provide an implementation if it is to be instantiable.

10. Abstract Class Cannot Be Instantiated

An abstract class cannot normally be used to create an object directly.

class Shape {

public:

    virtual void draw() = 0;
};

// Shape shape;  // Not allowed

Instead, a derived class can implement the pure virtual function.

11. Derived Class Implementing Abstraction

class Shape {

public:

    virtual void draw() = 0;

    virtual ~Shape() = default;
};

class Circle : public Shape {

public:

    void draw() override {

        std::cout <<
            "Drawing Circle";
    }
};

int main() {

    Circle circle;

    circle.draw();

    return 0;
}

The Shape class defines the interface, while Circle provides the implementation.

12. Abstraction Using Virtual Functions

Virtual functions allow a base class to define an interface that can be implemented differently by derived classes.

class Animal {

public:

    virtual void sound() = 0;

    virtual ~Animal() = default;
};

class Dog : public Animal {

public:

    void sound() override {

        std::cout <<
            "Dog barks";
    }
};

13. Abstraction and Polymorphism

Abstraction and polymorphism often work together.

class Animal {

public:

    virtual void sound() = 0;

    virtual ~Animal() = default;
};

class Dog : public Animal {

public:

    void sound() override {

        std::cout <<
            "Dog barks";
    }
};

class Cat : public Animal {

public:

    void sound() override {

        std::cout <<
            "Cat meows";
    }
};

The base class provides the abstract interface, while derived classes provide different implementations.

14. Abstraction with Base Class Pointer

class Shape {

public:

    virtual void draw() = 0;

    virtual ~Shape() = default;
};

class Circle : public Shape {

public:

    void draw() override {

        std::cout <<
            "Circle";
    }
};

int main() {

    Circle circle;

    Shape* shape = &circle;

    shape->draw();

    return 0;
}

The pointer uses the abstract interface instead of depending directly on implementation details.

15. Interface Through Abstract Class

An abstract class can define operations that every derived class is expected to provide.

class Payment {

public:

    virtual void pay(
        double amount
    ) = 0;

    virtual ~Payment() = default;
};

Every concrete payment class can implement pay() in its own way.

16. Payment System Example

class Payment {

public:

    virtual void pay(
        double amount
    ) = 0;

    virtual ~Payment() = default;
};

class CashPayment : public Payment {

public:

    void pay(
        double amount
    ) override {

        std::cout <<
            "Cash payment: "
            << amount;
    }
};

class CardPayment : public Payment {

public:

    void pay(
        double amount
    ) override {

        std::cout <<
            "Card payment: "
            << amount;
    }
};

The abstract class defines what a payment class must do, while the derived classes decide how to do it.

17. Abstraction with Encapsulation

Encapsulation and abstraction are related but different concepts.

Concept Main Purpose
Encapsulation Bundles data and functions together and controls access.
Abstraction Hides unnecessary implementation details and exposes essential behavior.

Both concepts help make programs easier to use and maintain.

18. Abstraction vs Encapsulation

Consider a bank account.

  • Encapsulation: The balance is kept private.
  • Abstraction: The user interacts with simple operations such as deposit and withdraw without knowing the internal banking logic.
class BankAccount {

private:

    double balance;

public:

    void deposit(double amount) {

        balance += amount;
    }

    void withdraw(double amount) {

        if(amount <= balance) {

            balance -= amount;
        }
    }
};

19. Real-World ATM Example

class ATM {

private:

    void verifyPIN() {

        std::cout <<
            "PIN verified";
    }

    void connectBank() {

        std::cout <<
            "Bank connected";
    }

public:

    void withdraw() {

        verifyPIN();

        connectBank();

        std::cout <<
            "Cash withdrawn";
    }
};

The user only calls withdraw(). The internal operations remain hidden.

20. Real-World Vehicle Example

class Vehicle {

public:

    virtual void start() = 0;

    virtual ~Vehicle() = default;
};

class Car : public Vehicle {

public:

    void start() override {

        std::cout <<
            "Car engine started";
    }
};

The user only needs to know that the vehicle can be started. The internal starting mechanism is hidden.

21. Multiple Pure Virtual Functions

An abstract class can contain multiple pure virtual functions.

class Shape {

public:

    virtual double area() = 0;

    virtual double perimeter() = 0;

    virtual ~Shape() = default;
};

A concrete derived class must implement both functions.

22. Practical Shape Abstraction

#include <iostream>

class Shape {

public:

    virtual double area() = 0;

    virtual void display() = 0;

    virtual ~Shape() = default;
};

class Rectangle : public Shape {

private:

    double length;
    double width;

public:

    Rectangle(
        double l,
        double w
    )
        : length(l),
          width(w) {
    }

    double area() override {

        return length * width;
    }

    void display() override {

        std::cout <<
            "Area: "
            << area();
    }
};

int main() {

    Rectangle rectangle(10, 5);

    rectangle.display();

    return 0;
}

The user of Rectangle can call area() without needing to know how the calculation is internally performed.

23. Abstraction with Access Specifiers

Access specifiers help control which parts of a class are visible to other code.

Specifier Access
public Accessible through the class interface.
private Accessible only inside the class and permitted friends.
protected Accessible inside the class and derived classes, subject to access rules.

Using access control helps hide implementation details.

24. Benefits of Abstraction

  • Reduces Complexity: Users do not need to understand internal details.
  • Improves Security: Internal data and implementation can be protected.
  • Improves Maintainability: Internal implementation can change without changing the public interface.
  • Encourages Reuse: Common interfaces can be used by multiple classes.
  • Improves Design: Classes can focus on clear responsibilities.
  • Supports Polymorphism: Abstract interfaces can work with different derived implementations.

25. Common Abstraction Mistakes

  • Trying to create an object of an abstract class.
  • Forgetting to implement pure virtual functions in a concrete derived class.
  • Exposing unnecessary internal data publicly.
  • Making a public interface unnecessarily complicated.
  • Confusing abstraction with encapsulation.
  • Adding too many unrelated responsibilities to an abstract class.
  • Forgetting a virtual destructor in an appropriate polymorphic base class.

26. Best Practices for Abstraction

  • Expose only the operations that users actually need.
  • Keep implementation details private where appropriate.
  • Use abstract classes for meaningful common interfaces.
  • Use pure virtual functions when derived classes must provide behavior.
  • Keep interfaces small and focused.
  • Use override in derived classes.
  • Use a virtual destructor for suitable polymorphic base classes.
  • Prefer clear and meaningful class responsibilities.

27. Abstraction and Interface Design

A good abstraction provides a simple and meaningful interface.

class Notification {

public:

    virtual void send(
        std::string message
    ) = 0;

    virtual ~Notification() = default;
};

Different notification classes can implement the same interface.

class EmailNotification :
    public Notification {

public:

    void send(
        std::string message
    ) override {

        std::cout <<
            "Email: "
            << message;
    }
};

28. Real-World Uses of Abstraction

  • Banking: Deposit, withdrawal, and transfer operations hide internal banking processes.
  • ATM: Users interact with simple options while internal operations remain hidden.
  • Payment Systems: Different payment methods implement a common payment interface.
  • Vehicles: Users operate controls without knowing all internal engine details.
  • Notification Systems: Email, SMS, and app notifications can share a common interface.
  • Graphics: Different shapes can implement common drawing operations.
  • Database Systems: High-level operations can hide complex database implementation details.

29. Abstraction Example with Payment Interface

#include <iostream>

class Payment {

public:

    virtual void pay(
        double amount
    ) = 0;

    virtual ~Payment() = default;
};

class UPI : public Payment {

public:

    void pay(
        double amount
    ) override {

        std::cout <<
            "UPI payment: "
            << amount
            << std::endl;
    }
};

class Card : public Payment {

public:

    void pay(
        double amount
    ) override {

        std::cout <<
            "Card payment: "
            << amount
            << std::endl;
    }
};

void processPayment(
    Payment& payment,
    double amount
) {

    payment.pay(amount);
}

int main() {

    UPI upi;

    Card card;

    processPayment(upi, 500);

    processPayment(card, 1000);

    return 0;
}

The processPayment() function works with the abstract Payment interface instead of depending on one specific payment implementation.

30. Abstraction – Final Summary

Concept Meaning
Abstraction Showing essential features while hiding unnecessary implementation details.
Abstract Class A class containing at least one pure virtual function.
Pure Virtual Function A virtual function declared with = 0.
Interface A set of operations through which code interacts with an object.
Encapsulation Bundling data and functions together and controlling access.
Polymorphism Allowing the same interface to represent different implementations.
class Shape {

public:

    virtual double area() = 0;

    virtual ~Shape() = default;
};

class Circle : public Shape {

private:

    double radius;

public:

    Circle(double r)
        : radius(r) {
    }

    double area() override {

        return 3.14159 *
               radius *
               radius;
    }
};

Here, Shape defines what a shape must provide, while Circle provides the actual implementation. This is a fundamental example of abstraction in C++.

📌 Key Points

  • Abstraction means hiding unnecessary implementation details.
  • It focuses on what an object does rather than how it does it.
  • Classes and access specifiers can help implement abstraction.
  • Private members can hide internal implementation details.
  • Abstract classes provide common interfaces for derived classes.
  • A pure virtual function is declared using = 0.
  • An abstract class cannot normally be instantiated directly.
  • Derived classes can implement pure virtual functions.
  • Abstraction and polymorphism often work together.
  • Good abstraction provides a simple, focused, and meaningful interface.

🧠 Quick Quiz

Question: What is the main purpose of abstraction in C++?