Abstraction and Interface

Submitted by coleen.yan@edd… on Mon, 04/22/2024 - 17:53
Sub Topics

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:

  1. 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.
  2. 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 function pow() 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:

A diagram depicting that abstraction and encapsulation are different

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 modifierprivate, 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:

  1. 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.

A diagram depicting 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.

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.

  1. All the different classes of shapes, such as balls and triangles, must be descended from a single base class.
  2. The draw() function must be declared to be virtual 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.

  1. Look at the interactive example program below and run the program to get the output.
  2. Remove the virtual keyword and note how the output changes.
  3. Change all the sequences of ‘draw’ to ‘print’.
  4. 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.

A diagram depicting that virtua function and pure virtual function are different

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
Two people playing with PS3 controls

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:

  1. Interface is part of a class that is directly accessible to its users. It defines what an object can do.
  2. 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 interface
  • private 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:

  1. .h: A "header" file containing only interface (declarations).
  2. .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.

  1. Interface inheritance allows different derived classes to be used interchangeably through interface provided by common base class.
  2. 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 diagram depicting an example of class hierarchies

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.

Module Linking
Main Topic Image
Two programmers discussing codes for work.
Is Study Guide?
Off
Is Assessment Consultation?
Off