What is Inheritance in OOP?
We may inherit mannerisms/behaviours, physical features and habits from our parents. Someone might tell you that you look exactly like your grandfather, or that your mannerisms are similar to your mothers. It’s the same in programming. Inheritance is simply taking properties, and methods from another class. Whenever we want to extend the functionality of an existing class, we use inheritance.
- Deriving a new class from an existing class is inheritance.
- The existing class is the base class.
- The new class is the derived class
- Every member (property and function) of the existing class will be inherited into the derived class except private members.
Inheritance is a way of creating a new class by starting with an existing class and adding new members. The new class can replace or extend the functionality of the existing class. Inheritance models the 'is-a' relationship between classes. It is just like saying "A is a B type of thing". For example, Apple is a Fruit, Car is a Vehicle etc. Inheritance is unidirectional. For example, House is a Building. But Building is not a House.
A
Arrow means derived from
B
Defined in derived class
C
Defined in base class but accessible from dervied class
Note: You may read different terminology when reading about inheritance. Some programmers like to use parent and child class terminology.
- Base class – parent (or superclass) – the class being inherited from
- Derived class – child (or subclass) – that inherits from another class
Inheritance is useful for code reusability. You can reuse attributes and methods of an existing class when you create a new class.
The Cherno describes it as a way of ensuring you don’t have to have the same code repeated multiple times. By creating a base class, subclasses can be created to access functions from that base class. He says that in OOP there’s a huge paradigm and inheritance between classes is one of the fundamentals and one of the most powerful features that can be leveraged. Watch his video to learn more.
To inherit from a class use the :
(semi colon) symbol.
Inheritance Syntax and Notation
Inheritance of Members
Class Access
A derived class can access all the non-private members of its base class. This means base-class members that should not be accessible to the member functions of derived classes should be declared private
in the base class.
A derived class inherits all base class methods with the following exceptions:
- Constructors, destructors and copy constructors of the base class.
- Overloaded operators of the base class.
- The friend functions of the base class.
Base Class Access Specification
Base class access specification determines how private
, protected
, and public
members of base class can be accessed by derived classes
C++ supports three types of inheritance modes, also called base class access modes:
- public inheritance - When deriving a class from a public base class,
public
members of the base class becomepublic
members of the derived class andprotected
members of the base class becomeprotected
members of the derived class. A base class'sprivate
members are never accessible directly from a derived class, but can be accessed through calls to thepublic
andprotected
members of the base class.
Syntax:class Child: public Parent {};
- protected inheritance - When deriving from a
protected
base class,public
andprotected
members of the base class becomeprotected
members of the derived class.
Syntax:class Child: protected Parent {};
- private inheritance - When deriving from a
private
base class,public
andprotected
members of the base class becomeprivate
members of the derived class.
Syntax:class Child: private Parent {};
Base Class Access vs Member Access specification:
Base class access is not the same as member access specification:
- A - Base class access: determine access for inherited members
- B - Member access specification: determine access for members defined in the class
Base class access specifiers:
public
– object of derived class can be treated as object of base class (not vice- versa)protected
– more restrictive thanpublic
but allows derived classes to know some of the details of parentsprivate
– prevents objects of derived class from being treated as objects of base class.
Type of inheritance (base class access specifier) | Private members | Protected members | Public members |
---|---|---|---|
private |
Inaccessible | Become private in derived class | Become private |
protected |
Inaccessible | Stay protected | Become protected |
public |
Inaccessible | Stay protected | Stay public |
Member Access Specification:
Specified using the keywords private
, protected
and public
.
Types of access | Meaning |
---|---|
private | Class members declared as private can be used be member functions and friends (classes or funcions) of the class. |
protected | Class members declared as protected can be used by member functions and friends (classes or functions) of the class. Additionally, they can be used by classes derived from the class. |
public | Class members declared as public can be used by any function. |
When a subclass takes functions from two base classes it is known as multiple inheritance. Information is taken from more than one base class to get an outcome in the new derived class. Codearchery uses the example of a child getting pocket money from both his mother and father. But there is a lot more to inheritance that is covered here, so these skilfully created videos are worth watching to get an all rounded understanding of how inheritance can be utilised.
- Single Inheritance in C++ - 39
- Inheritance in C++ - 38
- Access Specifiers in inheritance (must watch) - 40
- Multiple InheMultilevel Inheritance in C++ - 41
- Multiple Inheritance in C++ - 42
- Hierarchical Inheritance in C++ - 43
- Hybrid Inheritance in C++ - 44
Photo by Chris Ried on Unsplash
Inheriting Multiple Base classes:
It is possible for a derived class to inherit two or more base classes. Look at the following as an example of how derived inherits classes Base1
and Base2
. To inherit more than one base, use a comma separator list. Also be sure to use an access specifier for each base inherited.
Output:
10 20
If you haven’t already – watch the Multilevel Inheritance in C++ video from the list above.
© Learning works
Practice 1
Write a simple program to demonstrate public inheritance.
This example gives an explanation of how it works:
Output:
1 2 3
To explain the above program: the member functions that are declared under public void set()
and void show()
are available as public
in derived class derived. This means they can be accessed with the derived type object obj
outside the classes because they are public
.
Practice 2
The following program was written to demonstrate private inheritance. Explain what the output be and why.
Here’s the code:
!@#$
Example explanation:
The derived class derived inherits the base class in private
mode. This means the public functions in the base class – void set()
and void show()
become private
in derived class and can’t be accessed outside the derived class in the main()
function. Therefore when we try to access these in main()
the compiler throws an error.
Practice 3
Write a program to demonstrate what happens to protected
data members of a base class when they are inherited by a derived class in public
mode.
Remember that by using protected
you can create class members that are private
to their class yet can be inherited and accessed by a derived class.
Your example might look similar to this:
Output:
Compiler Error
Did you get a compiler error? Here’s why:
When protected
data members i
and j
of the base class are inherited in private
mode by the derived class derived they become private
in the derived class and are accessible within the derived class member function void setk() { k = i * j; }
. However the base class public member functions set()
and show()
when inherited in private
mode become private
in derived class and are not accessible outside the classes in main()
and thus the compiler generates error when a derived class object obj
tries to access them.
Practice 4: Now put these all together.
Implement a base class called vehicle:
- Decide data members and member functions
Derive two classes from the vehicle base class called car and truck. - Decide data members and member functions
You can read the tutorial on the the tenouk website as your starting point and progress through developing the program to its completion if you want to extend your understanding of class inheritance.
Overriding Base Class Functions
- Overriding: function in a derived class that has the same name and parameter list as a function in the base class
- Typically used to replace a function in base class with different actions in derived class
- Not the same as overloading – with overloading, the parameter lists must be different
Access to Overridden function:
- When a function is overridden, all objects of derived class use the overriding function.
- If necessary to access the overridden version of the function, it can be done using the scope resolution operator with the name of the base class and the name of the function:
Student::getName()
Constructors, Destructors, and inheritance
The process of creating and deleting objects in C++ is vital. Each time an instance of a class is created the constructor is called. These are used to initialize the objects of its class. It’s treated as a special member function as its name is the same as the class name. When the associated class is created, these constructors are invoked. Initial values can be passed as arguments to the constructor function when the object is declared in two ways, by calling the constructor explicitly or by calling the constructor implicitly.
Destructors are used to destroy the objects that have been created by the constructor within the program. The destructor doesn’t take argument nor does it return any value and the compiler implicitly invoked up the exit from the program for cleaning up storage that is no longer accessible - for example deletes files or expired links – to clear space in memory.
There are two points that come up relative to constructors and deconstructors when inheritance is involved:
- When are base-class and derived class constructors and deconstructors called?
- How can parameters be passed to base class constructors?
By inheriting every member of the base class, a derived class object contains a base class object.
The derived class constructor can specify which base class constructor should be used to initialize the base class object.
Order of execution/calling:
When an object of a derived class is created, the base class’s constructor is executed first, followed by the derived class’s constructor. When an object of a derived class is destroyed, its destructor is called first, then that of the base class.
A - Execute Student constructor, then execute UnderGrad constructor
B - Execute UnderGrad destructor, then execute Student
Constructors are executed in their order of derivation; destructors are executed in reverse order or derivation.
Order of constructor call | Order of destructor call |
---|---|
A {}Class A constructor |
C {}Class C constructor |
B {}Class B constructor |
B {}Class B constructor |
C {}Class C constructor |
C {}Class C constructor |
Prepinsta uses the following code with output to help explain this sequence.
Look at the example code below which shows the sequence using a parent and child class.
Take some time to practice some more of what you have learned.
Practice 5:
Create a project called Base
Create an Animal
class (Separate Compilation)
- Animal.h
- Animal.cpp
The member function makeNoise()
returns string "unknown"
.
Test your class by instantiating one or more Animal
objects inside base.cpp.
Reference: Header files in C++
Example base class:
Output:
Animal name: myPet Animal weight: 100 Animal noise: Unknown
Practice 6:
Derived classes with overridden functions - continue with Animal
class:
makeNoise()
to return "Woof!"
digHole()
to return "I am digging a hole!"
.Output:
Animal name: myPet Animal weight: 100 Animal noise: Unknown Dog's name: Rover Dog's weight: 80 Dog's noise: Woof! Dog's breed: Greyhound I am digging a hole!
Practice 7: Derived Cat
Class
Continue with earlier example:
makeNoise()
to return "Meow!"
chaseMouse()
to return "I am chasing a mouse!"
.Solution:
Output:
Animal name: myPet Animal weight: 100 Animal noise: Unknown Dog's name: Rover Dog's weight: 80 Dog's noise: Woof! Dog's breed: Greyhound I am digging a hole! Cat's name: Felix Cat's weight: 12 Cat's noise: Meow! I am chasing a mouse!
Passing Arguments to Bass Class Constructor
- Allows selection between multiple base class constructors
- Specify arguments to base constructor on derived constructor heading
- Can also be done with inline constructors
- Must be done if base class has no default constructor
To pass an argument to a constructor in a base class you use an expanded form of the derived class’s constructor declaration that passes along arguments to one or more base-class constructors.
For example:
You will see from the above example that a colon separates the constructor declaration of the derived class from the base classes and the base classes are separated from each other by commas, in the case of multiple base classes. Base1 through baseN are the names of the base classes inherited by the derived class. In general, the constructor of the derived class must declare the parameter(s) that its class requires as well as any required by the base class. Any parameters needed by the base class are passed to it in the bass class’s argument list which is specified after the colon.
And another example here:
And more in depth here:
Remember from earlier learning that the default constructor of the base class is called ONLY if you don’t specify which one to call.
For example, the default would look something like this:
Or you can explicitly call another constructor in the derived class:
Overriding base class functions:
- Overriding: function in a derived class that has the same name and parameter list as a function in the base class
- Typically used to replace a function in base class with different actions in derived class
- Not the same as overloading – with overloading, the parameter lists must be different
Access to overridden function:
- When a function is overridden, all objects of derived class use the overriding function.
- If necessary to access the overridden version of the function, it can be done using the scope resolution operator (
::
) with the name of the base class and the name of the function:
Read the following article to overriding and access examples, along with explanations of Function Overriding in C++.
Practice 8:
Write a C++ program to demonstrate the sequence in which constructors and deconstructors are invoked from the base class and the derived class.
Your example might look something like this:
Output:
Constructing base Constructing derived Destructing derived Destructing base
Practice 9:
Write a program to demonstrate the passing of parameters to base class constructor through derived class.
Output:
Constructing base Constructing derived 4 3 Destructing derived Destructing base
How did you get on? Are you able to follow your own logic as you build your program? For example, in the above program, the derived constructor takes two parameters x
and y
i.e. derived (int x, int y): base(y)
and passes one to the base class i.e. y
. Since show()
is under public
access specifier and it is available for access outside the classes in the main()
. Also the variable x
from the base class under protected
and can be accessed in the derived class in the show()
function also the mode of inheritance of derives class is public
.
Hint: It can help your progress greatly when you can explain in your own words, the logical way in which your program works.
Portfolio courses explain how constructors work with Inheritance in C++.
Watch the video:
and access their website, or github to find the source code used in the video.
The ‘this’ pointer:
The member functions of every object have access to a sort of magic pointer named this, which points to the object itself. Thus, any member function can find out the address of the object of which it is a member.
To understand this pointer, it’s important to know how objects look at functions and data members of a class.
- Each object gets its own member of the data member
- All-access the same function definition in the code segment.
Each object gets its own copy of data members, and all objects share a single copy of member function.
When you call a member function, it comes into existence with the value of this set to the address of the object for which it was called. The ‘this pointer can be treated like any other pointer to an object and can be used to access the data in the object it points to.
Accessing member with ‘this’
The tester()
member function accesses the variable alpha
as
This is the same as referring to alpha directly. This syntax works, but there is no reason for it except to show that this does indeed point to the object.
A more practical use for this is in returning values from member functions and overloaded operators.
Read the article from geeks for geeks to learn more about the ‘this’ pointer in C++.
Aggregation and composition are types of association and only represent a ‘part of’ relationship. You were introduced to UML class diagram relationships in the introduction to OOP in another part of the course so you may want to go back and relook at class relationships.
- Class aggregation: An object of one class owns an object of another class
- Class composition: A form of aggregation where the enclosing class controls the lifetime of the objects of the enclosed class
- Supports the modelling of ‘has-a’ relationship between classes – enclosing class ‘has a(n)’ instance of the enclosed class
Aggregation is called a “has a” relationship. We say a library has a book or an invoice has an item line. It’s a specialised form of association where all objects have their own lifecycle but there is ownership like parent and child. The child object can’t belong to another parent object at the same time – so it is considered to have a ‘has a’ relationship.
In object-oriented programming, aggregation may occur when one object is an attribute of another. Here’s a case where an object of class A
is an attribute of class B
:
The composition is a part-whole relationship where the parts are contained within the whole. In these parts, some objects are not compulsorily dependent on one object only. They have their independent meaning and existence. For example:
The headlight and engine of a car. The car is the main object, and the headlight and engine are present in a car, but this doesn’t mean that they don’t have their own existence. The headlight can be removed and put into another car.
Aggregation is represented by a hollow diamond attached to a straight line.
Association is a simple structural connection between classes and is a relationship where all objects have their own lifecycle and there is no owner. It is simply some kind of connection between two objects. The relationship may be in one direction or in both directions.
An example is where multiple students can associate with a single teacher and a single student can associate with multiple teachers. There is no ownership between the objects, and both have their own lifecycle, both can create and delete independently.
This relationship is represented by:
Object composition:
In this type of relationship, the parts of an object are completely dependent on it. Objects would not exist without the object. If we destroy the object, these parts will get destroyed too. Here parent and child objects have coincidental lifetimes. The child doesn’t not have its own lifecycle. If the parents object gets deleted, so does all it’s child objects.
Composition is a stronger form of aggregation. It has all the characteristics of aggregation, plus two more:
- Typically uses normal member variables.
- Can use pointer values if the composition class automatically handles allocation/deallocation.
- Responsible for the creation/destruction of subclasses.
This relationship is depicted using a solid diamond attached to a straight line.
Aggregation through pointers:
Typically, pointer variables are used to point to an object that lives outside the scope of the aggregate class.
- A ‘has-a’ relationship can be implemented by owning a pointer to an object
- Can be used when multiple objects of a class may ‘have’ the same attribute for a member
- ex: students who may have the same city/state/ zip code
- Using pointers minimizes data duplication and saves space
- Aggregation represents the owner/owned relationship between objects.
- Composition is a form of aggregation in which the lifetime of the owned object is the same as that of the owner object
- Owned object is usually created as part of the owning object’s constructor, destroyed as part of owning object’s destructor
Member Initialisation Lists
In C++ whenever an object is created, its constructor is called, but also, its parent class is called as are all the constructors for all the objects that belong in the class. By default, the constructors invoked are the default - no-argument constructors. In the definition of a constructor of a class, member initializer lists specify the initializers for direct and virtual bases and non-static data members.
- Member Initialization lists can be used to simplify the coding of constructors
- Should keep the entries in the initialization list in the same order as they are declared in the class
- Allows constructor for enclosing class to pass arguments to the constructor of the enclosed class
You will recall that constructors and destructors interact with class hierarchies. The constructor builds a class object from the bottom up, may use it and then it’s destroyed after. This ordering ensures that the base or a member isn’t used before it’s been initialized or used after it’s been destroyed. Constructors can establish invariants and pick up resources. They do this by initializing class members and base classes.
The members constructors are called before the body of the containing class’s own constructor is executed. The constructors are called in the order in which the members are declared in the class rather than in the order in which the members appear in the initializer list. If the initializers are not in the member declaration order, the compiler should send up a warning.
Think about a class that is used to hold information for a small club or organization.
In this example, the Club’s constructor takes the name of the club and its founding date as arguments. Arguments for a member’s constructor are specified in a member initializer list in the definition of the constructor of the containing class.
For example:
The member initializer list starts with a colon, and the individual member initializers are separated by commas.
There are two ways to initialize data members of classes. CppNuts outlines both methods in this video, Initializer List in C++ and further in What Are All Those Places Where Initializer List Is Must In C++.
And Portfolio Courses gives a comprehensive explanation of how, when and why to use member initializer lists in C++.
Note: it’s not always necessary to define a constructor for a class when they are relatively simple. Users can initialize objects of a class (or struct) using uniform initialization. Where a class has no constructor, you provide the list elements in the order that the members are declared within the class. If the class does have a constructor, you would provide the elements in order of the parameters. You can learn more about brace initialization (with empty braces) in the Microsoft Visual Studio Library and then continue to initializer_list class outlines.
A class diagram provides a clear visualization of the mapping within Object-Oriented Programming (OOP) languages, particularly reflecting Object-Oriented Concepts as depicted in UML:
- Class: A class serves as the blueprint, defining the structure and functions of an object.
- Objects: Objects facilitate the decomposition of large systems, aiding in modularizing the system. Modularity assists in breaking down the system into comprehensible components, enabling incremental system development. An object serves as the fundamental unit or building block of a system, representing an entity.
- Inheritance: Inheritance enables child classes to inherit properties from their parent classes.
- Abstraction: Abstraction involves concealing implementation details from users.
- Encapsulation: Encapsulation involves binding data together and safeguarding it from external access.
- Polymorphism: Polymorphism allows functions or entities to exist in various forms.
Source: 12IT302CV Object Oriented Analysis and Design, CVR College of Engineering Handouts
It is generally used for construction purposes and would happen at the beginning of the program planning cycle.
Class diagrams are used for:
- describing the static view of the system
- showing the collaboration among the elements of the static view
- describing the functionalities performed by the system
- construction of software applications using OOP’s.
A class may be involved in one or more relationships with other classes and can be one of the following types:
Inheritance (or generalization) relationship:
- A class may be involved in one or more relationships with other classes.
- The base class name is shown as
SuperClass
. SubClass1
andSubClass2
are specializations ofSuperClass
.
- The base class name is shown as
- The figure below shows an example of inheritance hierarchy.
SubClass1
andSubClass2
are derived fromSuperClass
.- The relationship is displayed as a solid line with a hollow arrowhead that points from the child element to the parent element.
The figure below shows an inheritance example with two styles.
Although the connectors are drawn differently, they are semantically equivalent
Association
Associations are relationships between classes in a UML Class Diagram. They are represented by a solid line between classes. Associations are typically named using a verb or verb phrase which reflects the real-world problem domain.
Simple Association:
A structural link between two peer classes.
There is an association between Class1
and Class2
The figure below shows an example of simple association. There is an association that connects the <control>
class Class1
and <boundary>
class Class2
. The relationship is displayed as a solid line connecting the two classes.
Cardinality
Cardinality is expressed in terms of:
- one to one
- one to many
- many to man
Special type of association:
It represents a "part of" relationship.
Class2
is part of Class1
.
Many instances (denoted by the *) of Class2
can be associated with Class1
.
Objects of Class1
and Class2
have separate lifetimes.
The figure below shows an example of aggregation. The relationship is displayed as a solid line with a unfilled diamond at the association end, which is connected to the class that represents the aggregate.
Composition:
A special type of aggregation where parts are destroyed when the whole is destroyed.
Objects of Class2
live and die with Class1
.
Class2
cannot stand by itself.
The figure below shows an example of composition. The relationship is displayed as a solid line with a filled diamond at the association end, which is connected to the class that represents the whole or composite.
Dependency:
An object of one class might use an object of another class in the code of a method. If the object is not stored in any field, then this is modelled as a dependency relationship.
A special type of association exists between two classes if changes to the definition of one may cause changes to the other (but not the other way around).
Class1
depends on Class2
The figure below shows an example of dependency. The relationship is displayed as a dashed line with an open arrow.
The figure below shows another example of dependency. The Person
class might have a hasRead()
method with a Book parameter that returns true if the person has read the book (perhaps by checking some database).
Realisation:
Realization is a relationship between the blueprint class and the object containing its respective implementation level details. This object is said to realize the blueprint class. In other words, you can understand this as the relationship between the interface and the implementing class.
For example, the Owner interface might specify methods for acquiring property and disposing of property. The Person
and Corporation
classes need to implement these methods, possibly in very different ways.
Class diagram – order system:
Geeks for geeks outlines the rules around UML modelling:
The UML has a number of rules that specify what a well-formed model should look like. A well-formed model is one that is semantically self-consistent and in harmony with all its related models.
The UML has semantic rules for:
- Names – What you can call things, relationships, and diagrams.
- Scope – The context that gives specific meaning to a name.
- Visibility – How those names can be seen and used by others.
- Integrity – How things properly and consistently relate to one another.
- Execution – What it means to run or simulate a dynamic model.
Remember that UML is not a programming language but is often described as a visual language that portrays the behaviour and structure of a system. It helps people with modelling, design and analysis.
You can choose to watch Geekific as he explains UML Class and object diagrams in a video. The code snippets can be found at github.
Read the article, UML Class Diagram Explained With C++ samples, from CPP CODE TIPS. There is a step-by-step process to create a UML diagram with code examples.