Hello there! Welcome to our lesson on understanding inheritance in C++. Today, we're going to explore an important concept in programming called inheritance.
Why Inheritance?
Kids inherit certain traits from their parents, like eye color or hair color. In programming, inheritance works in a similar way. It allows one class (the child class) to inherit properties and behaviors from another class (the parent class). This helps us write more efficient and organized code by reusing existing code.
By the end of this lesson, you will understand what inheritance is in C++, how it works, and how to use it to create organized and reusable code.
In C++, we use the :
symbol to indicate inheritance. For now, we'll use the public
keyword to specify public inheritance. We'll discuss other inheritance options in the next lesson. Let's look at a basic example:
C++1#include <iostream> 2 3class Cat { 4public: 5 void voice() { 6 std::cout << "Meow!" << std::endl; 7 } 8}; 9 10class Lion : public Cat { 11}; 12 13class Tiger : public Cat { 14};
In this example:
Cat
is the base class.Lion
andTiger
are derived classes inheriting fromCat
.- Both
Lion
andTiger
can use thevoice()
method from their parent classCat
.
Let's take a look at the main function example:
C++1int main() { 2 Cat cat; 3 Lion lion; 4 Tiger tiger; 5 6 cat.voice(); // Output: Meow! 7 lion.voice(); // Output: Meow! 8 tiger.voice(); // Output: Meow! 9 10 return 0; 11}
In Lion
and Tiger
, we don't have to define the voice()
method; they have it by default because they are inherited from the Cat
class.
Let's add some attributes to our base class:
C++1#include <iostream> 2 3class Cat { 4public: 5 std::string color; 6 int age; 7 8 void voice() { 9 std::cout << "Meow!" << std::endl; 10 } 11}; 12 13class Lion : public Cat { 14}; 15 16class Tiger : public Cat { 17};
Now, Cat
is defined by its color and age. These attributes are also derived by the Lion
and the Tiger
classes.
Let's see how we can use them:
C++1int main() { 2 Lion lion; 3 Tiger tiger; 4 5 lion.color = "Golden"; 6 lion.age = 5; 7 8 tiger.color = "Orange"; 9 tiger.age = 3; 10 11 std::cout << "Lion: " << lion.color << ", " << lion.age << " years old" << std::endl; // Output: Lion: Golden, 5 years old 12 std::cout << "Tiger: " << tiger.color << ", " << tiger.age << " years old" << std::endl; // Output: Tiger: Orange, 3 years old 13 14 return 0; 15}
Both Lion
and Tiger
can access the color
and age
attributes inherited from the Cat
class.
Sometimes, we need to change the behavior of a base class method in a derived class. This is known as function overriding. To do this, we must mark the method in the base class as virtual
and use the override
keyword in the derived class. Using override
helps to avoid errors by ensuring that the derived class method is actually overriding a base class method.
Example:
C++1#include <iostream> 2 3class Cat { 4public: 5 virtual void voice() { 6 std::cout << "Meow!" << std::endl; 7 } 8}; 9 10class Lion : public Cat { 11public: 12 void voice() override { 13 std::cout << "Roar!" << std::endl; 14 } 15}; 16 17class Tiger : public Cat { 18public: 19 void voice() override { 20 std::cout << "Grr!" << std::endl; 21 } 22};
Tiger
and Lion
classes redefine the voice
method to have their own unique behavior.
Now, each type of cat has a unique voice:
C++1int main() { 2 Cat cat; 3 Lion lion; 4 Tiger tiger; 5 6 cat.voice(); // Output: Meow! 7 lion.voice(); // Output: Roar! 8 tiger.voice(); // Output: Grr! 9 10 return 0; 11}
In some cases, we may need to add methods to a derived class that are not present in the base class. This way, we can extend the base class' functionality. This can be done simply by defining the new method in the derived class. Let's add a unique method to the Lion
class:
C++1#include <iostream> 2 3class Cat { 4public: 5 virtual void voice() { 6 std::cout << "Meow!" << std::endl; 7 } 8}; 9 10class Lion : public Cat { 11public: 12 void voice() override { 13 std::cout << "Roar!" << std::endl; 14 } 15 16 void hunt() { 17 std::cout << "Lion is hunting!" << std::endl; 18 } 19};
In this example:
- The
Lion
class has a unique method calledhunt()
. - This method is not available in the
Cat
class.
Let's see how it works:
C++1int main() { 2 Cat cat; 3 Lion lion; 4 5 cat.voice(); // Output: Meow! 6 lion.voice(); // Output: Roar! 7 lion.hunt(); // Output: Lion is hunting! 8 // cat.hunt(); // Uncommenting this line will cause a compilation error as 'Cat' doesn't have a 'hunt' method 9 10 return 0; 11}
Note:
- When calling
lion.hunt()
, we get the output specific to theLion
class, showing how to extend the base class functionality with new methods. - We can't call
cat.hunt()
, becausehunt()
is the new method of the extendedlion
class.
The base class constructor is called first when a derived class is instantiated. Let's see how constructors work in inheritance:
C++1#include <iostream> 2 3class Cat { 4public: 5 std::string color; 6 int age; 7 8 Cat(std::string c, int a) : color(c), age(a) { 9 std::cout << "Cat constructor called" << std::endl; 10 } 11 12 virtual void voice() { 13 std::cout << "Meow!" << std::endl; 14 } 15}; 16 17class Lion : public Cat { 18public: 19 Lion(std::string c, int a) : Cat(c, a) { 20 std::cout << "Lion constructor called" << std::endl; 21 } 22 23 void voice() override { 24 std::cout << "Roar!" << std::endl; 25 } 26};
In this example:
- The
Cat
class has a constructor that initializes thecolor
andage
attributes. - The
Lion
class inherits fromCat
and also has its own constructor. - The
Lion
constructor calls theCat
constructor using the initializer list syntax.
Let's examine our constructors:
C++1int main() { 2 Lion lion("Golden", 5); 3 4 // Output: 5 // Cat constructor called 6 // Lion constructor called 7 8 std::cout << "Lion: " << lion.color << ", " << lion.age << " years old" << std::endl; // Output: Lion: Golden, 5 years old 9 lion.voice(); // Output: Roar! 10 11 return 0; 12}
When an object of the Lion
class is created, the Cat
constructor is called first, followed by the Lion
constructor.
By calling the base class constructor from the derived class constructor, we ensure the base class attributes are properly initialized.
Just as constructors are called in a specific order in an inheritance hierarchy, so are destructors. When a derived class object is destroyed, the derived class destructor is called first, followed by the base class destructor. This ensures that any additional cleanup required by derived classes is performed before the base class destructor is executed.
Example:
C++1#include <iostream> 2 3class Cat { 4public: 5 Cat() { 6 std::cout << "Cat constructor called" << std::endl; 7 } 8 virtual ~Cat() { 9 std::cout << "Cat destructor called" << std::endl; 10 } 11 virtual void voice() { 12 std::cout << "Meow!" << std::endl; 13 } 14}; 15 16class Lion : public Cat { 17public: 18 Lion() { 19 std::cout << "Lion constructor called" << std::endl; 20 } 21 ~Lion() { 22 std::cout << "Lion destructor called" << std::endl; 23 } 24 void voice() override { 25 std::cout << "Roar!" << std::endl; 26 } 27};
The Cat
destructor is marked as virtual
to ensure that the derived class destructor is called first.
Let's see it in action:
C++1int main() { 2 Lion lion; 3 4 // Output: 5 // Cat constructor called 6 // Lion constructor called 7 // Lion destructor called 8 // Cat destructor called 9 10 return 0; 11}
In this example:
- When the
lion
object is destroyed, theLion
destructor is called first, followed by theCat
destructor. - This ensures that any resources allocated by derived classes are properly released before the base class destructor runs.
Today, we explored the concept of inheritance in C++. We learned about:
- The importance and syntax of inheritance.
- How to customize inherited methods through function overriding.
- The role of constructors and destructors in inheritance.
Great job on getting through the lesson! Now, it's time to put your knowledge into practice. In the upcoming practice sessions, you will create your own base and derived classes, override methods, and use them accordingly. This hands-on experience will help solidify your understanding of inheritance in C++. Let's get started!