Back to: C++
What is abstraction?
Abstraction is a process that is used to display only the essential information and to hide everything else. We use this technique to separate the interface from the implementation.
In C++, classes provide a level of data abstraction. Classes provide public methods to the outside world to use with the functionality of the object and to manipulate object data, i.e., state without actually knowing how the class has been implemented internally.
Real-world analogy
Imagine a driver pressing on the accelerator of their car. The driver understands that by pressing on the accelerator, the speed of the car will increase. What the driver does not need to understand is how pressing on the accelerator causes an increase in speed! Also, there are different methods for making a car increase in speed when the accelerator is pressed – the driver does not know what method is being used! The driver not knowing how the accelerator works and what method is used is an example of abstraction. The driver understands the interface but does not need to understand the implementation.
Code example
The cout object (of the class ostream) is used to display text to a user’s screen. When using cout we don’t need to know and understand the implementation of cout. In fact, the implementation of cout can be changed but you would not need to know this or to make any changes to your code.
Classes
We can implement abstraction in classes. Classes help us to group data members and member functions using available access specifiers. A class can decide which data member will be visible to the outside world and which is not.
Header files
Another type of abstraction are header files. There is pow() method that is contained in the math.h header file. The pow() method allows us to calculate the power of a number. Whenever we need to calculate the power of a number, we call the function pow() and pass the numbers as arguments, without knowing the underlying algorithm according to which the function is actually calculating the power of numbers.
Abstraction using access specifiers
Access specifiers are the main pillar of implementing abstraction. We can use access specifiers to enforce restrictions on class members.
- Members declared as public in a class can be accessed from anywhere in the program.
- Members declared as private in a class can be accessed only from within the class. They are not allowed to be accessed from any part of the code outside the class.
#include <iostream>
using namespace std;
class implementAbstraction
{
private:
int a, b;
public:
// method to set values of
// private members
void set(int x, int y)
{
a = x;
b = y;
}
void display()
{
cout<<"a = " <<a << endl;
cout<<"b = " << b << endl;
}
};
int main()
{
implementAbstraction obj;
obj.set(10, 20);
obj.display();
return 0;
}
Output:
a = 10
b = 20
In the above program you cannot access variables a and b directly. However, you can call the function set(), to set the values in a and b and the function display(), to display the values of a and b.
Benefits of Data Abstraction
Abstraction provides several benefits. By defining data members only in the private section of the class, the class author is free to make changes to the data. If the implementation changes, only the class code needs to be examined to see what effect the change may have. If data is public, then any function that directly accesses the data members of the old representation might be broken.
- Helps the user to avoid writing the low-level code
- Avoids code duplication and increases reusability
- Can change the internal implementation of class independently, without affecting the user
- Helps to increase the security of a program, as only important details are provided to the user
- Class internals are protected from inadvertent user-level errors, which might corrupt the state of the object
- The class implementation may evolve over time in response to changing requirements or bug reports without requiring a change in user-level code
Why would you use Abstraction?
The abstraction separates the interface code from the implementation code. Therefore, when we are designing components, we must keep the interface independent of the implementation. This way, if we change the underlying implementation, the interface will remain intact! We would recompile with the new implementation code and there would be no changes to the interface code!