C++: Inheritance and Friends
Inheritance is an important concept in C++ when working with classes, which essentially allows one class to inherit functionality from another.
The easiest way to express this concept is for me to show you an example. Firstly, let's create a basic class called "Fish". This is going to be a really generalized class which contains some basic, generic information about fish which we'd generally want in all objects that are of type "Fish". For the sake of simplicity, let's just structure and implement at the same time:
1 2 3 4 5 6 7 8 9 10 |
|
We're going to refer to this class as the base class. We're going to do this, because the plan is that we're going to create a few classes for a few different types of fish - all of which we want to possess the basic properties and functions shown in our "Fish" class, but we also want them to have some unique functionality of their own. If we wanted to we could just copy/paste everything in our "Fish" class into any of these classes (Perhaps "Cod" and "Haddock"), however this repetition should be triggering your bad programming senses, and would also mean that if you wanted to make a small change to the base of all the fish classes, you'd have to modify the version of code present in each individual class.
To solve this issue, we can simply make some new classes and tell them to inherit from the "Fish" base class. This can do different things depending on the access modifier used to inherit from the base class, but for the most part it'll just give the "child" class (which inherits from the base) all the public
and protected
functionality from the base class - the private
stuff will also be "inherited" so to speak, however will only be accessible by the the stuff in the base class. Just to re-iterate the protected
section, which we glossed over before - its purpose is to provide a way to give access to data and functions to inherited classes and "friends" - so not open to everyone like public
, and not closed to everyone (apart from "friends") like private
.
As I alluded to earlier, when you inherit from a base class you must also specify an access modifier to inherit with. Inheriting with public
is by far the most common, and is what you'll use most of the time -- essentially, public
stuff stays public, private
stuff stays private, and protected
stuff stays protected. Inheriting with the private
access modifier makes all public
stuff private
, and leaves the other private
and protected
stuff alone, and inheriting with protected
makes all public
stuff protected
, and leaves all private
stuff and other protected
stuff alone.
With this in mind, we can make a class inherit from a base class by putting a colon after the class name, followed by the access modifier we're inheriting with, followed by the base class name. So we could make a "Cod" class that inherits from "Fish" with public
access restriction like so:
1 2 3 4 5 |
|
As we inherited with the "public" access modifier, any "Cod" objects will have access to all the public
member functions we made in "Fish" -- "speed_up", "slow_down", and "output". It's worth noting that the constructor(s) and destructor(s) aren't "copied across" in the process so to speak, however they are still called during the relevant processes. The "Fish" constructor is called in the construction process of a "Cod" object for example, as could be seen by putting a cout
in the constructor and then creating a "Cod" object.
The whole point of all "Cod" objects not just being "Fish" is that there is something unique about "Cod" which makes them need to be classified differently. As such, we should probably give "Cod" objects some unique functionality. Since we declared the "speed" integer in a protected
section, we can access "speed" with members functions inside the "Cod" class (we wouldn't be able to if it was private
), and as such, perhaps the unique thing about "Cod"s is that they start with a speed of 10 -- we could specify this in our "Cod" constructor:
1 2 3 4 5 |
|
You can also overload/"overwrite" some of the base class functions if you want to. So if we simply re-write a function with the same name and parameters in an inherited class, it will be used instead of the base class version - say for example that "Cod"s should actually speed up by 20 each time:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
|
From running the code above, you should see that "bob" brags a speed of 30 from speeding up once (since he started at 10 due to the constructor then added 20), whereas "freddie" (the 'regular' fish) only has a speed of 10 (he started at 0 and then added 10). Also note that the constructor initialization list method of setting member variables can also be used to call the constructor of the base class. This becomes more useful when base constructors are more complex , however in this example could be used to utilize the base constructor to set the speed:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
|
Obviously our "Fish" class isn't super practical, but it makes for a nice demonstration of the concept. We could also add custom variables and functions in different restrictive sections - so maybe "Cod"s should have a scale count whereas regular "Fish"es shouldn't. Try to add this functionality on your own as well as adding a "Haddock" class with some unique functionality if you're up to the task.
An even bigger and better task which you may wish to try and accomplish, is making a very simple text-based RPG (Role Playing Game), or at least some classes that might power such a system. You might want some classes for players, NPCs, enemies, weapons, and so on, and you should try to use inheritance to represent the "is a" relationships, so a gun is a weapon, and so will probably want to inherit (publicly) from the "weapon" class.
The "Weapon" class, for example, might have a basic "attack" member function which has a general formula for how much damage to do to the enemy. A nice way of actually doing this damage to the enemy (which, let's just assume is an object of the "Enemy" class) is for the "attack" member function to take an "Enemy" pointer. The ->
operator in C++ with regards to pointers essentially represents actions on the object a pointer is pointing to, so a->b
is equal to (*a).b
, and as such this kind of functionality could be utilized in an "attack" member function. This functionality is often used in a variety of applications and so is something I recommend you try to get used to. An example of some classes I whipped up for my little RPG are as follows:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 |
|
You should be able to see how the above could then be expanded upon with extra logic and classes and fancy things, to create a very basic proper RPG! To really show off the inheritance we should probably add some more classes for things like swords, and maybe magic attacks, but I'll leave those for you to do.
To finish off this tutorial, we're just going to talk a little bit about friend classes and friend functions. This essentially just gives some class(es) or some function(s) access to private
(and protected
), as well of course to the public
sections of a class! This is done by using the friend
keyword inside of the class who's private
and protected
sections you wish to share, and then writing the function prototype, or the class
keyword followed by the class name. That's pretty much all there is to it.
So in the simple RPG snippet presented earlier, let's pretend we also added an "NPC" class which handles the functionality of non-playable characters (perhaps we have dialogue and other fancy things stored in some multi-dimensional arrays or something). Since we want NPCs to be able to modify weapon data (for enchantments or whatever), however we don't want NPCs to inherit from the "Weapon" class, we can make the "NPC" class a friend of the "Weapon" class by using the line friend class NPC;
in the "Weapon" class. As "Gun" inherits from "Weapon", "Gun" will also treat "NPC" as a friend class (although the NPC's member functions may need to have a different version to work with "Gun" objects). To show this, the modified version of the "Weapon" class in our example as well as the new "NPC" class are below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
|
The above could then be utilized in the "main" function by creating a new NPC and just seeing the damage difference to the zombie from them enchanting the gun (or any other weapon).