Abstraction
Abstraction is a concept of object-oriented programming that helps represent only the important features of a program without including background details or explanations. It represents essential features without including details that we don’t want others to see, so it essentially hides information from the users.
A real-life example of this might be your television. You have a remote which allows you to turn it on and off, change the channel, adjust the volume and you can plug in external devices such as Chromecast, DVD players and Pc’s. But as a user you have no idea how it receives signals or how it translates them to display the picture on the screen. And you don’t have to know – you only want to watch TV! So, the internal functions are hidden from you, but you can play with the remote to watch your favourite shows and dvd’s or even scroll through Facebook. Relative internal functions are sorted and selected so they can be made available to the many apps downloaded onto the SMART TV and the relevant operations are used over a wide range of plug in features. This process is referred to as abstraction.
There are two ways that abstraction can be implemented:
- Abstraction using classes and objects: access specifiers on data members /member functions. Abstraction can be implemented using classes which helps us group data members and member functions using available access specifiers. A class can decide which data members will be visible and which won’t.
- Abstraction through header files. Using the
pow()
method in the math.h header file, as an example. Whenever the program needs to calculate the power of a number, we can simply call the functionpow()
present in the math.h header file and pass the numbers as arguments without having to know the underlying function that is actually doing the calculation.
The following example show data abstraction using the header file math.h:
Output:
The square of a is: 25
Read the following article from w3schools for their explanation of data abstraction before continuing.
Access specifiers are the main way we implement abstraction in C++. The access specifiers enforce restrictions on class members.
- Members declared as
public
can be accessed from anywhere in the program. - Members declared as
private
can be accessed only from within the class – they can’t be accessed from any part of the code outside the class.
Notice in the example program below that we cannot access the variables a
and b
directly, but we 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
:
Output:
a = 10 b = 20
Advantages of data abstraction:
- Increased security of a program or application as only relevant details are provided for user.
- Stops duplication of code.
- Easy to reuse program code.
- Allows changes to be made internally within a class independently from the user experience.
What’s the difference between Abstraction and Encapsulation?
Encapsulation is the process of making a complex system easier for the user to use. For example, a telephone has lots of features that can be used on it – such as a camera, but we don’t need to know how it all works before we can take a photo. Encapsulation is like a wrapper that protects random access from outside of that wrapper.
While abstraction and encapsulation may seem similar there are some main differences that you should be aware of:
Abstraction | Encapsulation |
---|---|
Process or method of gaining information. | Process or method to contain the information. |
Problems solved at design or interface level. | Problems solves at the implementation level. |
Method of hiding the unwanted information. | Hide data in single entity or unit along with method to protect information from outside access. |
Implement abstraction using abstract class and interfaces. | Can be implemented using access modifier – private , public , and protected . |
Implementation complexities hidden using abstract classes and interfaces. | Data hidden using methods of getters and setters. |
Objects that help perform abstraction are encapsulated. | Objects that result in encapsulation need not be abstracted. |
Watch the video from Trevor Payne on abstract and interface classes. He also has a whole series of other helpful video tutorials that you may like to view at your leisure.
Virtual Functions:
Virtual functions overcome the problems with the type-field solution by allowing the programmer to declare functions in a base class that can be redefined in each derived class. An example of what that looks like is below:
Virtual means existing in appearance but not in reality. When virtual
functions are used, a program that appears to be calling a function of one class may in reality be calling a function of a different class.
A virtual
function is nothing but a member function of a base class that you redefine in a derived class. The property of redefining a member function declared in the base class to a derived class is also called Function Overriding.
The following link takes you to a program called Base Class with one derived class.
Use these instructions to change the program and run them to find the new output:
- Add a
virtual
function and re-run the program. What is the new output?
Diagram 1: Shows how the compiler ignores the contents of the pointer ptr
and chooses the member function that matches the type of the pointer.
Diagram 2: With virtual
function, the member functions of the derived classes, not the base class, are executed.
Why do we need virtual
functions?
Suppose a graphics program includes several different shapes: a triangle, a ball, a square, and so on as in say the MULTISHAPE program. In this MULTISHAPE program we have base class Shape
and derived class Circle
, Rectangle
, Triangle
etc. Each of these classes has a member function draw()
that causes the object to be drawn on the screen. Now suppose you plan to make a picture by grouping a number of these elements together, and you want to draw the picture in a convenient way.
One approach is to create an array that holds pointers to all the different objects in the picture. For example:
Completely different functions are executed by the same function call.
- If the pointer in
ptrarr
points to a ball, the function that draws a ball is called - if it points to a triangle, the triangle-drawing function is called.
This is called polymorphism, which means many forms. The functions have the same appearance, the draw()
expression, but different actual functions are called, depending on the contents of ptrarr[j]
.
For the polymorphic approach to work, several conditions must be met.
- All the different classes of shapes, such as balls and triangles, must be descended from a single base class.
- The
draw()
function must be declared to bevirtual
in the base class.
Compare the two class base programs below to see if you can identify the piece/s of code that creates the difference seen in the output from the programs.
Program 1:
Output:
Base Function Derived Function
Program 2:
Output:
Derived Function Derived Function
Write what the output for each program below is, in the spaces provided.
Program 1:
Answer:
Print something
Program 2:
Answer:
Print something specific
Now you can practice yourself on this shapes program.
- Look at the interactive example program below and run the program to get the output.
- Remove the
virtual
keyword and note how the output changes. - Change all the sequences of ‘draw’ to ‘print’.
- Add another derived class called ‘STAR’ including functions and pointer to add it to the output. Run the program to ensure it works correctly.
Let’s look at virtual
functions in video form to get another perspective from Codearchery.
And then follow up with Abstract Class and Pure Virtual function in C++ - 51 to remind you of how virtual
functions work in programming before we continue.
Virtual functions (Practice 1)
Open the program below and follow the instructions noted in the code.
Pure Virtual functions:
An abstract class is a class with a pure function in it. And once you declare a pure virtual
function it is compulsory for you to override this in any derived class you create. If you don’t override the pure virtual
function, then the derived class becomes an abstract class too and you end up with an error.
A pure virtual
function in C++ is a virtual
function for which we don’t have implementation. We can only declare it. It is declared by assigning a 0
at the end of the declaration. For example:
A class with a pure virtual
function is called an abstract class and no objects of that abstract class can be created.
Bucky explains Abstract Classes and Pure virtual
functions in this video.
In the previous videos you learned about virtual
function and pure virtual
functions. You were also introduced to early vs late binding. Here we will explain how Early and Late Binding dictates the outcome of a program with the use of the virtual
keyword.
These two concepts are related to Polymorphism which you should have already had an introduction to, in another topic. Early Binding (or static binding) occurs at compile time while the Late Binding occurs at runtime. The key difference is that Early Binding uses the class information to resolve method calling and is done by default in C++ while Late Binding uses the object to resolve method calling and is achieved with the help of virtual
keyword.
What is Binding?
Binding for functions means that wherever there is a function call the compiler needs to know which function definition it should call. This can depend on the signature of each function declaration and the arguments it’s taking, and the compiler needs to know when this function binding happens.
Early Binding checks the methods or properties during compile time. The compiler converts high level language into low level language that computers can understand, machine - language. In early binding the compiler chooses a function declaration out of many functions with different signatures/arguments at compile time. As the name suggests, this binding happens early before the program runs. Therefore, early binding is an example of compile-time polymorphism.
Output:
In Base class
Late Binding:
With the virtual
function the compiler doesn’t know what class the contents of ptr
may contain. It could be the address of an object of the more than one class. Which version of draw()
does the compiler call?
At runtime, when it is known what class is pointed to by ptr
, the appropriate version of draw will be called. This is called late Binding or dynamic binding.
Late Binding requires some overhead but provides increased power and flexibility. Remember that Late Binding is achieved with the help of virtual
keyword.
For example:
Output:
In Derived class
Data abstraction is creating classes with appropriate functions which hide unnecessary details.
In C++, when writing classes, you must understand the separation of interface and implementation:
- Interface is part of a class that is directly accessible to its users. It defines what an object can do.
- Implementation is part of class that its users access only indirectly through interface. This carries out the instructions defined in the interface.
Access specifiers:
public
members constitute class interfaceprivate
members constitute class implementation
Interface:
Describes the behaviour and capabilities of a class without committing to a particular implementation of that class. C++ interfaces are implemented using abstract classes and these should not be confused with data abstraction which is a concept of keeping implementation separate from associated data.
Abstract classes can’t be used to instantiate objects, it serves only as an interface. Attempting to do this will cause a compilation error. If a subclass needs to be instantiated, it must implement each of the virtual
functions, which means it supports the interface declared in the abstract class.
Read the article on Interfaces including the drawbacks and significance of Interfaces in C++ here
Implementation: Definitions of how the above are implemented.
C++ implements this separation using two kinds of code files:
- .h: A "header" file containing only interface (declarations).
- .cpp: A "source" file containing definitions.
When you define a new class Foo, you write Foo.h and Foo.cpp.
Derived Classes:
The design style supported by abstract classes is called interface inheritance in contrast to the implementation inheritance supported by base classes with state and/or defined member functions.
- Interface inheritance allows different derived classes to be used interchangeably through interface provided by common base class.
- Implementation inheritance saves implementation effort by sharing capabilities provided by base class.
Pure interface inheritance will be if your interface class has only pure virtual
functions. In contrast, if your base class has data members or implemented functions, you have an implementation inheritance.
Class Hierarchies:
A class hierarchy is a set of classes ordered in a lattice created by derivation (e.g., : public
). inheritance relationships between classes form what is called class hierarchy
A smiley face is a kind of a circle which is a kind of a shape.
The arrows represent inheritance relationships. For example, class Circle is derived from class Shape.
A class hierarchy offers two kinds of benefits:
Interface inheritance: An object of a derived class can be used wherever an object of a base class is required. That is, the base class acts as an interface for the derived class to allow different derived classes to be used interchangeably through the interface provided by a common base class.
Interface inheritance is often referred to as run-time polymorphism(or dynamic polymorphism).
Implementation inheritance: A base class provides functions or data that simplifies the implementation of derived classes. Smiley’s uses of Circle’s constructor and of Circle::draw()
are examples. Such base classes often have data members and constructors to save implementation effort by sharing facilities provided by a base class.
- Use base classes with implementations of
virtual
functions to support implementation inheritance interface (an abstract class) and its implementations (derived class)
This video from the Creative Channel gives a good explanation of Interface vs Implementation.