π§βπInheritance and Polymorphism
Inheritance is the capability of one class to acquire the properties of another. It allows the developer to reuse the code by creating a new class that inherits the functionality of an existing class. It serves as a mechanism for code re-usability.
Polymorphism means the ability of an object to take many forms. The most common use of polymorphism in C++ occurs when a parent class reference is used to refer to objects of a child class. When a function or operator is called using a parent class reference, it is the version of the function or operator defined in the child class that is called, thus allowing the child class object to have polymorphic behavior.
Inheritance Concept
Inheritance is an important concept in OOP which allows us to define a class in terms of another class, thereby providing code reusability. In inheritance, we divide the program into a hierarchical order of classes.
The main idea behind inheritance is that you define a class which inherits the properties and behaviors of another class. The class whose properties are inherited is called the base class or parent class. The class that inherits the other class is called the derived class or child class. This allows code reusability and prevents the repetition of code.
In C++, inheritance is achieved through the use of the colon (:) symbol. The syntax is:
The derived class inherits all the members (properties and behaviors) of the base class except the constructors and destructors. The derived class can add its own fields and methods and can also override the base class methods.
Inheritance provides the reusability of a code and provides a security as the base class data can only be accessed through derived class functions. It also makes the program modular by dividing it into manageable parts. Inheritance provides an is-a relationship between classes.
Hope this helps explain the concept of inheritance in CPP! Let me know if you have any other questions.
Inheritance in C++
Here are the main concepts regarding inheritance in C++
Class Hierarchy: Inheritance establishes a class hierarchy between base and derived classes. The base class sits at the top of the hierarchy and derived classes inherit from the base class.
Types of Inheritance:
Single Inheritance: A derived class inherits from only one base class.
Multilevel Inheritance: A derived class inherits from another derived class. This forms a chain of classes.
Hierarchical Inheritance: A base class is inherited by multiple derived classes.
Multiple Inheritance: A derived class inherits from more than one base class.
Access Specifiers: We use access specifiers to specify the accessibility of the members of the base class in the derived class. The access specifiers are:
public: Members are accessible everywhere
protected: Members are accessible within the class and its derived classes
private: Members are accessible only within the class
Inheritance Features:
Code reusability
Type safety (inherited members can only be accessed through objects of the derived class)
Support for IS-A relationship between classes
Compile time binding (binding happens at compile time)
Hope this summary helps explain inheritance in C++! Let me know if you have any other questions.
Protected Members
Protected members in C++ are members of a class that can only be accessed by:
The class itself
Classes derived from it
Friends of the class
Protected members allow you to hide implementation details within a class hierarchy, while still allowing derived classes to access those members.
The main benefits of protected members are:
Encapsulation: Protected members encapsulate data within a class and its derived classes, hiding the implementation from the outside world. This results in more maintainable and robust code.
Controlled accessibility: By making members protected, we control which classes can access and modify those members.
Reusability: Derived classes can reuse the protected members of the base class.
The syntax for a protected member is:
Only classes derived from the class containing the protected members, and the class itself, can access protected members.
For example:
So in summary, protected members allow us to share data within a class hierarchy, while hiding implementation details from outside classes.
Hope this explanation helps! Let me know if you have any other questions.
Base Class Initializer List
Here is an example of using a base class initializer list in C++ :
Here we have a base class Base and a derived class Derived. In the constructor of Derived, we use a base class initializer list to initialize the base class Base as:
Derived(int x, int y, int z) : Base(x, y), c(z)
This initializes the base class Base with the arguments (x, y) and then initializes the member c of the derived class.
The benefits of using a base class initializer list are:
Efficiency - It initializes base class members before the constructor body is executed.
Correctness - It ensures members of base classes are correctly initialized before the constructor body.
Eliminates ambiguity - Without an initializer list, the compiler would not know whether to call the base class default constructor or a derived class constructor.
So in summary, base class initializer lists allow you to initialize base class members before the constructor body is executed, ensuring correct initialization of base class members.
Hope this example helps explain base class initializer lists! Let me know if you have any other questions.
Composition
Here is an example of composition in C++ :
Output:
In this example, we have two classes:
Engine : Represents the car engine
Car: Represents the car itself
The Car class has an Engine object as a member variable. This denotes a composition relationship - a Car "has-a" Engine.
The benefits of composition over inheritance are:
Maintainability - Compositions are usually more robust to changes than inheritances.
Reusability - Composed objects can be reused in different contexts.
Flexibility - We can change the composed object at runtime.
Modularity - It divides a system into independent modules.
In summary, composition is a type of association between classes where one class owns another class. The composed class delegates work to the component class, allowing for high cohesion and loose coupling.
Hope this example helps explain composition! Let me know if you have any other questions.
Member Initialization List
Here is an example of using a member initialization list in C++ :
Output:
1, 2
In the Point constructor, we use a member initialization list to initialize the member variables x and y:
Point(int x, int y) : x(x), y(y)
This initializes x with the argument x and y with the argument y.
The benefits of using a member initialization list are:
Efficiency - It initializes members before the constructor body is executed.
Correctness - It ensures members are initialized with the correct values passed as arguments.
Eliminates ambiguity - Without an initialization list, the compiler would initialize members after the constructor body, potentially overriding any initialization done in the body.
In summary, member initialization lists allow you to initialize member variables before the constructor body is executed, ensuring they are initialized with the intended values.
Hope this helps explain member initialization lists! Let me know if you have any other questions regarding C++ programming.
Order of Initialization
The order of initialization in C++ is:
Base class constructors are called
Data members are initialized via initialization lists
Derived class constructors are called
Member functions are executed
For example:
Output:
Here we can see:
Base class constructor is called first
Then the member
a
is initialized via initialization listThen the derived class constructor body is executed
Finally, the member function
print()
is called
The order of initialization ensures that:
Base class members are initialized before derived class members
Members are initialized before they are used
Constructors are called in the proper order from base to derived classes.
Hope this helps explain the order of initialization in C++! Let me know if you have any other questions.
Inheritance vs. Composition
Here is example code demonstrating the difference between inheritance and composition in C++:
Inheritance:
Output:
Here the Car class inherits from the Vehicle class, forming an "is-a" relationship.
Composition:
Output:
Here the Car class composes a Vehicle object, forming a "has-a" relationship.
Key differences:
Inheritance:
Creates an "is-a" relationship
Members of the base class become members of derived class
Child class relies on parent class
Composition:
Creates a "has-a" relationship
Child class has a reference to parent class
Child class is independent of parent class
Hope this example code helps illustrate the difference between inheritance and composition in C++. Let me know if you have any other questions!
A Case for Polymorphism
Here is an example demonstrating the need for polymorphism in C++:
Now say we have a function that needs to draw a shape:
We can call it like this:
This will print:
So by passing a base class pointer but calling the derived class draw()
method, we achieved polymorphism - letting the object decide which draw()
method to call at runtime.
The benefits of polymorphism are:
Code reusability - We can call the same function
draw_shape()
for different shapes.Loose coupling - The
draw_shape()
function does not need to know the concrete shape class.Extensibility - We can add new shape classes and call
draw_shape()
without modifying existing code.
So in summary, polymorphism allows us to call different implementations of the same method, based on the type of object at runtime. This makes our code reusable, extensible and decoupled.
Hope this example demonstrates the need for polymorphism! Let me know if you have any other questions regarding C++ programming.
Dynamic Binding
//Output:
This example demonstrates dynamic binding in C++. The base class Shape has a virtual function draw(). When a derived class object is assigned to the base class pointer, the correct overridden draw() function is called at runtime, based on the actual type of the object.
Pointer Conversion in Inheritance
Here is an example of pointer conversion in inheritance in C++ :
Output:
Here we have:
base pointer of type Shape * points to a Rectangle object
This performs pointer conversion
When we call base->draw(), the overridden Rectangle ::draw() is called
We then cast base to Rectangle * using dynamic_cast
If the cast is valid, derived will point to the Rectangle object
We call derived->draw() which again calls Rectangle ::draw()
So pointer conversion allows us to access derived class functions using a base class pointer. dynamic_cast performs a safe runtime cast.
Hope this helps! Let me know if you have any other questions
Polymorphism Using Dynamic Binding
Output:
This demonstrates polymorphism using dynamic binding in C++.
We have:
A base class Shape with a virtual function draw()
Derived classes Rectangle and Circle override draw()
A base class pointer s that points to Rectangle and Circle objects
When we call s->draw(), the correct overridden draw() is called based on the actual object type - this is dynamic binding.
So at compile time, the compiler sees a call to Shape::draw() But at runtime, the function call is resolved to the appropriate overridden draw() based on the object type - Rectangle::draw() or Circle::draw().
Virtual Function Specification
In C++, virtual functions are functions that can be overridden by derived classes. They are declared using the virtual
keyword in the base class, and the override
keyword in the derived class.
Here is an example code that demonstrates the use of virtual functions:
In this example, we have a base class Animal
with a virtual function speak()
, which is overridden by the derived classes Dog
and Cat
. We create instances of each class and call their speak()
function, which produces different output depending on the class type.
Note that we use pointers to Animal
objects to create instances of Dog
and Cat
. This is because virtual functions work with pointers and references to objects, not with objects themselves.
Invoking Virtual Functions
In C++, virtual functions can be invoked through a pointer or reference to an object. When a virtual function is called through a pointer or reference, the version of the function that is called depends on the type of the actual object pointed to or referred to. Here is an example code that demonstrates how to invoke virtual functions:
In this example, we create an instance of the Animal
class and call its speak()
function. This produces the output "Animal speaks!"
. We then create instances of the Dog
and Cat
classes and call their speak()
functions through a reference and a pointer to Animal
, respectively. This produces the output "Woof!"
and "Meow!"
, respectively. The version of the speak()
function that is called depends on the type of the object that the pointer or reference points to or refers to.
VTable in C++
In C++, a virtual table (vtable) is a mechanism used to implement dynamic dispatch of virtual functions. It is a table of function pointers that is created by the compiler for each class that has virtual functions, and it is used to resolve which version of a virtual function should be called at runtime.
Each object of a class with virtual functions has a hidden pointer, called the vpointer or vptr, that points to the vtable of the class. When a virtual function is called through an object pointer or reference, the vptr is used to locate the correct entry in the vtable, which contains a pointer to the correct version of the function.
Here is an example code that demonstrates how a vtable is used to implement dynamic dispatch:
In this example, the vtable for each class with virtual functions is created by the compiler, and each object of these classes has a vptr that points to its vtable. When a virtual function is called through an object pointer or reference, the vptr is used to locate the correct entry in the vtable, which contains a pointer to the correct version of the function. This allows the correct version of the function to be called at runtime, depending on the actual type of the object pointed to or referred to.
Virtual Destructors
In C++, a virtual destructor is a destructor that is declared as virtual in a base class. When a class with virtual functions is used as a base class, it is recommended to make the destructor virtual to ensure that the destructors of all derived classes are called properly.
Here is an example code that demonstrates the use of virtual destructors:
In this example, we define two classes: Animal
and Dog
. The Animal
class has a virtual destructor, which is declared using the virtual
keyword. The Dog
class has a non-virtual destructor.
In the main()
function, we create an object of the Dog
class and assign it to a pointer of the Animal
class. We then delete the object using the delete
operator. Since the Animal
class's destructor is declared as virtual, the destructor of the Dog
class is called properly, even though the object is being deleted through a pointer of the base class.
The output of this program is:
This shows that the destructors of both the base class and the derived class are called in the correct order, with the derived class's destructor being called first.
Abstract Class Using Pure Virtual Function
In C++, an abstract class is a class that has at least one pure virtual function, which is a virtual function that is declared using the = 0
syntax. An abstract class cannot be instantiated, and it can only be used as a base class for other classes.
Here is an example code that demonstrates the use of an abstract class with a pure virtual function:
In this example, we define an abstract class Shape
with a pure virtual function area()
. This function is declared with the = 0
syntax, which makes Shape
an abstract class that cannot be instantiated.
We then define two classes that derive from Shape
: Rectangle
and Circle
. Both of these classes implement the area()
function, which is required since it is a pure virtual function in the Shape
class.
In the main()
function, we create instances of Rectangle
and Circle
and call their area()
functions to calculate their areas.
Note that attempting to create an instance of the abstract class Shape
using the new
operator will result in a compilation error.
Last updated