Object Oriented Programming: What is Inheritance, Polymorphism, and Abstract Classes


Object-oriented programming (OOP) is an important concept to understand as a software developer. Most of the software you will encounter or work with will employ OOP in some way.

 

There are many concepts that are derived from OOP. Inheritance and polymorphism are two concepts derived from OOP that shows up frequently in development. The focus of this post is inheritance and polymorphism and the various ideas behind it.


 

Inheritance

 

Inheritance allows you to define a class in terms of another class, which makes it easier to create and maintain an application. To use inheritance, you start with a base (parent) class and then derive a child class from the base class. Inheritance works under the “is a” relationship model.

 

What is unique about an “is a” relation is that it works only in one direction, which is a child to parent. The following diagram demonstrates the “is a” relationship for some animals.

 

 

The base class is Animal and the derived classes (children) are Dog, Cat, and Monkey. Translating one of the relationships into a statement, you would get along the lines of “A monkey is an animal”. Notice that the other way around doesn’t work. The phrase, “An animal is a monkey” is not a true statement.

 

What Is Inherited from the Parent

 

There are access-specifiers that you can use in the base class to determine what the child class inherits. There are three main access-specifiers (some languages might have more), which are public, protected, and private.

 

AccessPublicProtectedPrivate
Same ClassYYY
Derived ClassYYN
Outside ClassYNN
Accessibility of variables and functions based on Access specifiers

 

  • Public: allows access in the same class, derived class, and outside classes
  • Protected: allows access in the same class and derived class
  • Private: allows access in the same class only

 

Polymorphism

 

Polymorphism means having many forms. Usually, polymorphism is desirable when there is a hierarchy of classes that are related by inheritance. Polymorphism allows the methods to keep the same function declaration, but have different implementations. This allows code to remain consistent and have dynamic behaviors.

 

Consider the following example:

The base class has a function called speak. The child classes, Cat and Dog, has their own implementation of speak. When a cat speaks, it goes “Meow”, while when a dog speaks, it goes “Woof”.

 

Abstract Classes

 

Sometimes implementation of all functions cannot be provided in a base class because we don’t know the implementation. A class that falls into such a situation is an abstract class. For example, consider an Animal class. We cannot provide an implementation for speak, but we know any children (cat, dog, monkey, etc.) must have an implementation for speak. Functions that do not have an initial implementation are called pure virtual functions or abstract functions.

 

There is some important information to keep in mind when using abstract classes. They are the following:

  • A class is abstract if it contains at least one pure virtual function
  • You cannot create an object of an abstract class. You can have pointers and references of an abstract class type though.
  • If the derived class doesn’t override the pure virtual function, then the derived class is also abstract
  • You can have constructors in the abstract class

 

Virtual Function Example

 

The following example is about inheritance and polymorphism. The base class is Animal and the child class is Cat. The virtual function is speak and is overridden in the Cat class. This example will also show how virtual functions behave when it comes to compile time resolution and runtime resolution.

 

#include <iostream>

class Animal {
    private:
        // empty
    protected:
        // empty
    public:
        
        /**
         * constructor
         */
        Animal() {
            // do nothing
        }

        /**
         * Have the animal speak.
         */
        virtual void speak() {
            std::cout << "*nothing to say*" << std::endl;
        }

        void print() {
            std::cout << "Animal class" << std::endl;
        } 
};

class Cat: public Animal {
    private:
        // empty
    protected:
        // empty
    public:

        /**
         * constructor
         */
        Cat() {
            // do nothing
        }

        /**
         * Have the animal speak.
         */
        void speak() {
            std::cout << "Meow" << std::endl;
        }

        void print() {
            std::cout << "Cat class" << std::endl;
        }
}; 

int main(int argc, char** argv) {
   
    Animal* animal_ptr;
    Cat cat;
    animal_ptr = &cat;

    // virtual function binds at runtime
    animal_ptr->speak();

    // non-virtual function binds at compile time
    animal_ptr->print();

    return 0;
}

 

When the program is compiled and run, you will get the following output:

 

Meow
Animal class

 

Notice that the virtual function speak was correct, but not the print function. This is because a virtual function call is determined at runtime, while a non-virtual function call is determined at compile time.

 

Abstract Class Example

 

The following example is the same example as in the Virtual Function Example section. The difference is that the speak function is changed into a pure virtual function, which makes the Animal class an abstract class.

 

#include <iostream>

/**
 * Abstract class
 */
class Animal {
    private:
        // empty
    protected:
        // empty
    public:
        
        /**
         * constructor
         */
        Animal() {
            // do nothing
        }

        /**
         * Have the animal speak.
         */
        virtual void speak() = 0;

        void print() {
            std::cout << "Animal class" << std::endl;
        } 
};

class Cat: public Animal {
    private:
        // empty
    protected:
        // empty
    public:

        /**
         * constructor
         */
        Cat() {
            // do nothing
        }

        /**
         * Have the animal speak.
         */
        void speak() {
            std::cout << "Meow" << std::endl;
        }

        void print() {
            std::cout << "Cat class" << std::endl;
        }
}; 

int main(int argc, char** argv) {

    // cannot have object of abstract class
    //Animal animal;
    
    Animal* animal_ptr;
    Cat cat;
    animal_ptr = &cat;

    // virtual function binds at runtime
    animal_ptr->speak();

    // non-virtual function binds at compile time
    animal_ptr->print();

    return 0;
}

 

When the program is compiled and run, you get the following output:

 

Meow
Animal class

 

Since the Animal class is abstract the Cat class must implement the speak function. If it does not then the Cat class will also become an abstract class.

 

You also cannot create an object of an abstract class. You will get a compiler error if you try to do so. Here is the error that g++ gives when trying to create an Animal object:

 

abstract_ex.cc: In function ‘int main(int, char**)’:
abstract_ex.cc:59:12: error: cannot declare variable ‘animal’ to be of abstract type ‘Animal’
     Animal animal;
            ^
abstract_ex.cc:6:7: note:   because the following virtual functions are pure within ‘Animal’:
 class Animal {
       ^
abstract_ex.cc:23:22: note:     virtual void Animal::speak()
         virtual void speak() = 0;

 


 

I hope you found this post helpful. If you found this post helpful, share it with others so they can benefit too.

 

What do you find to be the most challenging with object-oriented programming?

 

To stay in touch, follow me on Twitter, leave a comment, or send me an email at steven@brightdevelopers.com.

 

Additional Resources


About Steven To

Steven To is a software developer that specializes in mobile development with a background in computer engineering. Beyond his passion for software development, he also has an interest in Virtual Reality, Augmented Reality, Artificial Intelligence, Personal Development, and Personal Finance. If he is not writing software, then he is out learning something new.