Ready to practice?
Sign up to access interactive coding exercises and track your progress.
Extending Base Class Behavior
Add new member functions and data to derived classes.
Adding New Functionality to a Derived Class
In the Introduction to Inheritance lesson, we mentioned that one of the greatest advantages of using derived classes is the ability to reuse existing code. You can inherit the base class functionality and then add new features, modify existing features, or hide features you don't want. In this and the next few lessons, we'll examine more closely how each of these things is accomplished.
First, let's start with a simple base class:
#include <iostream>
class Entity
{
protected:
int m_health{};
public:
Entity(int health)
: m_health{ health }
{
}
void describe() const { std::cout << "I am an Entity\n"; }
};
Now, let's create a derived class that inherits from Entity. Because we want the derived class to be able to set the health when derived objects are instantiated, we'll make the Enemy constructor call the Entity constructor in the initialization list.
class Enemy: public Entity
{
public:
Enemy(int health)
: Entity{ health }
{
}
};
Adding new functionality to a derived class
In the above example, because we have access to the source code of the Entity class, we could add functionality directly to Entity if we desired.
There may be times when we have access to a base class but don't want to modify it. Consider the case where you've just purchased a library of code from a third-party vendor, but need some extra functionality. You could add to the original code, but this isn't the best solution. What if the vendor sends you an update? Either your additions will be overwritten, or you'll have to manually migrate them into the update, which is time-consuming and error-prone.
Alternatively, there may be times when it's not even possible to modify the base class. Consider the code in the standard library. We aren't able to modify the code that's part of the standard library. But we are able to inherit from those classes, and then add our own functionality into our derived classes. The same goes for third-party libraries where you are provided with headers but the code comes precompiled.
In either case, the best answer is to derive your own class, and add the functionality you want to the derived class.
One obvious omission from the Entity class is a way for the public to access m_health. We could remedy this by adding an access function in the Entity class -- but for the sake of example we're going to add it to the derived class instead. Because m_health has been declared as protected in the Entity class, Enemy has direct access to it.
To add new functionality to a derived class, simply declare that functionality in the derived class like normal:
class Enemy: public Entity
{
public:
Enemy(int health)
: Entity{ health }
{
}
int getHealth() const { return m_health; }
};
Now the public will be able to call getHealth() on an object of type Enemy to access the value of m_health.
int main()
{
Enemy goblin{ 50 };
std::cout << "Goblin has " << goblin.getHealth() << " health\n";
return 0;
}
This produces the result:
Goblin has 50 health
Although it may be obvious, objects of type Entity have no access to the getHealth() function in Enemy. The following does not work:
int main()
{
Entity entity{ 100 };
std::cout << "Entity has " << entity.getHealth() << " health\n";
return 0;
}
This is because there is no getHealth() function in Entity. Function getHealth() belongs to Enemy. Because Enemy is an Entity, Enemy has access to stuff in Entity. However, Entity does not have access to anything in Enemy.
Summary
Adding new functionality: Derived classes can add new member functions and member variables beyond what the base class provides. These additions are declared and defined normally in the derived class, just like any other class member. The derived class has access to protected and public base class members, allowing new functionality to work with inherited data.
When to extend vs modify base: If you have access to and control over the base class source code, you could add functionality directly to it. However, extending through derived classes is preferable when: you don't control the base class (third-party libraries, standard library), modifying the base would require migrating changes through updates, or you want specialized functionality that doesn't belong in the general base class.
Access to base class members: New derived class functions can access protected and public members from the base class directly. In the example, Enemy::getHealth() directly returns m_health, which was declared as protected in the Entity base class, allowing derived classes to access it while keeping it hidden from the public.
One-way relationship: Derived classes have access to base class members (subject to access specifiers), but base classes have no access to derived class members. An Entity object cannot call Enemy::getHealth() because Entity doesn't know anything about Enemy, even though Enemy knows about Entity through inheritance.
Adding new functionality to derived classes enables specialization and customization without modifying or duplicating base class code, making inheritance a powerful tool for code reuse and extension.
Extending Base Class Behavior - Quiz
Test your understanding of the lesson.
Practice Exercises
Extending Employee Class
Create an Employee base class with name and salary (protected). Create a Manager derived class that adds a department member and a method to calculate annual compensation (salary * 12).
Lesson Discussion
Share your thoughts and questions
No comments yet. Be the first to share your thoughts!