What is Abstraction?
Abstraction is the process of hiding the complex implementation details of a system while exposing only the necessary functionalities or interfaces to the outside world. It allows developers to focus on what an object does rather than how it does it.
Think of a car: You use the steering wheel, brake, and accelerator - but you don't need to understand the internal combustion engine to drive it.
- Abstraction is achieved through abstract classes in C++.
- It allows programmers to create a blueprint or template for objects without having to worry about their specific implementation details, making the system easier to use and manage.
Real-Life Analogy
Let’s take a coffee machine as an example.
- You press a button: "Espresso"
- The machine grinds beans, heats water, controls pressure…
- But you don’t need to know any of that
This is abstraction. You interact with a simple interface while the complex operations are hidden inside.
Key Features of Abstraction
- Hiding Implementation Details: The goal of abstraction is to hide the complex implementation details of a system and provide a simpler interface for interaction. For example, when you drive a car, you don't need to know how the engine works internally; you just need to know how to start the car, steer, and stop.
- Abstract Methods: These are methods declared in abstract classes or interfaces but have no body (i.e., they have no implementation). The implementation must be provided by the subclass or the class that implements the interface.
- Concrete Methods: These are methods with complete implementation in an abstract class. Subclasses inherit these methods, but they are also allowed to override them if necessary.
Achieving Abstraction
1️⃣ Abstract Classes
In C++, abstraction is often achieved using abstract classes. An abstract class is a class that cannot be instantiated and may contain one or more pure virtual functions.
An abstract class has at least one pure virtual function.
class Shape {
public:
virtual void draw() = 0; // Pure virtual function
};
- You cannot instantiate
Shape
. It only provides a blueprint.
Pure Virtual Function
A pure virtual function is a virtual function with no implementation provided in the base class. It serves as a placeholder for functions that must be implemented by derived classes.
Pure virtual functions are functions declared with the virtual keyword and assigned a = 0 specifier in the abstract class.
Complete Example:
#include <bits/stdc++.h>
using namespace std;
// Abstract class
class Animal {
public:
void eat() {
cout << "This animal eats food." << endl;
}
virtual void sound() = 0; // Pure virtual function
};
// Dog class
class Dog : public Animal {
public:
void sound() override {
cout << "The dog barks." << endl;
}
};
// Cat class
class Cat : public Animal {
public:
void sound() override {
cout << "The cat meows." << endl;
}
};
int main() {
Animal* myDog = new Dog();
Animal* myCat = new Cat();
myDog->eat();
myDog->sound();
myCat->eat();
myCat->sound();
delete myDog;
delete myCat;
return 0;
}
Explanation:
- The Animal class is abstract and defines both an abstract method sound() (which has no body) and a concrete method eat() (which has a body).
- The Dog and Cat classes are concrete classes that extend the Animal class. They provide their own implementations for the abstract sound() method.
- When we create objects of Dog and Cat, we can call both the inherited concrete eat() method and the overridden sound() method.
Interfaces
Interfaces in C++ are abstract classes with only pure virtual functions. They define a contract that concrete classes must adhere to by implementing all the functions declared in the interface.
Consider the following snippet:
#include <bits/stdc++.h>
using namespace std;
// Interface
class Animal {
public:
virtual void sound() = 0;
virtual void eat() = 0;
};
// Dog class implementing Animal interface
class Dog : public Animal {
public:
void sound() override {
cout << "The dog barks." << endl;
}
void eat() override {
cout << "The dog eats food." << endl;
}
};
int main() {
Animal* myDog = new Dog();
myDog->eat();
myDog->sound();
delete myDog;
return 0;
}
Explanation:
- The Animal interface defines two abstract methods: sound() and eat().
- The Dog class implements the Animal interface and provides concrete implementations for both methods.
- When we create an object of type Dog, we can call the eat() and sound() methods that were defined in the interface and implemented by the Dog class.
Difference between Abstract Class and Interface
Definition:
- Abstract Class: An abstract class is a class that cannot be instantiated on its own and may contain both concrete and abstract (pure virtual) member functions. Abstract classes can have member variables and constructors.
- Interface: An interface in C++ is represented by an abstract class containing only pure virtual functions. It provides a contract that classes implementing the interface must adhere to, but it does not contain any member variables or concrete function implementations.
Usage:
- Abstract Class: Abstract classes are used when you want to provide a common base implementation for a group of related classes, while allowing specific subclasses to provide their own implementations for certain methods. Abstract classes can have some common behavior that is shared among subclasses.
- Interface: Interfaces are used when you want to define a contract or set of behaviors that multiple unrelated classes must implement. Interfaces define what a class can do without specifying how it does it, promoting loose coupling between classes.
Inheritance:
- Abstract Class: Classes can inherit from abstract classes and provide concrete implementations for the pure virtual functions. Subclasses may also inherit member variables and concrete member functions from the abstract class.
- Interface: Classes implement interfaces by providing concrete implementations for all the pure virtual functions declared in the interface. A class can implement multiple interfaces, but it cannot inherit from more than one class.
Flexibility:
- Abstract Class: Abstract classes provide more flexibility than interfaces because they can contain both concrete and abstract member functions, as well as member variables. Subclasses can choose which methods to override and which to inherit from the abstract class.
- Interface: Interfaces promote a higher level of abstraction and decoupling by enforcing a strict contract without allowing any concrete implementations. Classes implementing an interface must adhere to the contract defined by the interface, providing a more uniform and predictable behavior.
Example:
Abstract Class
// Abstract class representing an animal
class Animal {
public:
// Pure virtual function to make this class abstract
virtual void makeSound() const = 0;
// Concrete function shared among all animals
void move() const {
std::cout << "The animal moves.\n";
}
// Virtual destructor
virtual ~Animal() {}
};
// Concrete subclass representing a dog
class Dog : public Animal {
public:
// Implementation of makeSound specific to dogs
void makeSound() const override {
std::cout << "Woof!\n";
}
};
// Concrete subclass representing a cat
class Cat : public Animal {
public:
// Implementation of makeSound specific to cats
void makeSound() const override {
std::cout << "Meow!\n";
}
};
Interface Class
// Interface representing an electronic device
class Device {
public:
// Pure virtual function to make this class an interface
virtual void turnOn() const = 0;
virtual void turnOff() const = 0;
// Virtual destructor
virtual ~Device() {}
};
// Concrete class representing a smartphone
class Smartphone : public Device {
public:
// Implementation of turnOn specific to smartphones
void turnOn() const override {
std::cout << "Smartphone turned on.\n";
}
// Implementation of turnOff specific to smartphones
void turnOff() const override {
std::cout << "Smartphone turned off.\n";
}
};
// Concrete class representing a laptop
class Laptop : public Device {
public:
// Implementation of turnOn specific to laptops
void turnOn() const override {
std::cout << "Laptop turned on.\n";
}
// Implementation of turnOff specific to laptops
void turnOff() const override {
std::cout << "Laptop turned off.\n";
}
};
Abstraction vs Encapsulation
Aspect | Abstraction | Encapsulation |
---|---|---|
What it hides | Implementation complexity | Internal data |
Focus | What an object does | How to protect data |
Achieved by | Abstract classes, interfaces | Access specifiers (private/protected) |
Both work together to build robust and scalable software.
Can Abstract Classes Inherit from Another Abstract Class
Yes
class AbstractBase {
public:
virtual void foo() = 0; // Pure virtual
};
class DerivedAbstract : public AbstractBase {
public:
// Does NOT implement foo(), still abstract
virtual void bar() = 0; // New pure virtual function
};
Here, DerivedAbstract
is also abstract because foo()
and bar()
are both pure virtual and not implemented.
Do We need to Implement all the pure virtual functions
A class remains abstract until it implements all the pure virtual functions (i.e., abstract methods) inherited from its base classes.
class A {
public:
virtual void foo() = 0; // pure virtual
};
class B : public A {
// Still abstract because foo() is not implemented
};
class C : public B {
public:
void foo() override { cout << "Implemented\n"; }
// Now C is concrete (non-abstract)
};
A
andB
are abstract.Only
C
can be instantiated, because it implements all pure virtual methods