The Penguin Programmer
Often a situation arises where one class is a natural extension of another. Taking part of the RPG tutorial as an example, in a fantasy game the players would be able to collect
Items, but also
Armour. All would share properties in common such as weight and monetary value, but the
Weapons would also know about damage and the
Armour about defense. Of course we could just write three separate classes for each of these and add the same properties to each of them, but a more efficient approach is to use inheritance.
Item class would be the base or parent class and would contain all the properties that the three classes share. The
Armour classes would then inherit those properties from the parent
Item class; they are the derived or child classes. When one class inherits from another, it takes on all the member variables and functions of its parent (constrained by their access specifiers).
There’s nothing new in the base class, but in the derived class we specify that it inherits from
Item using the
public access specifier, and there’s also a small modification to the constructor. Just like inside a class, an access specifier is used when inheriting.
public causes all
public members in the base class to also be
public in the derived class, but
private members of the base class are not accessible and are not declared in the derived class.
private makes all
public members in the base class
private in the derived class, and again the
private base class members are not declared.
Now we can create an instance of the
Weapon class and access its member variables which include those from the
Member functions are also inherited, as well as member variables, but some functions are a much more interesting case.
It would be helpful in this hypothetical fantasy game if the
Items could be described to the player, so we write a
describe member function in the
Item class which prints the weight and value of the object. It’s inherited in the
Weapon class, so is callable from there too.
But what about the
Weapon’s damage? Item doesn’t know anything about the properties of its derived classes, so we have to implement a different
describe function in
Weapon. We can’t do that though—unless we change the name or arguments—because a
describe function inherited from
Item already exists in
Weapon. This is where virtual functions step in. By declaring a function to be
virtual we tell the compiler that it will be redefined in a derived class, fixing the naming conflict.
And then in
Weapon we write the specialised
Here we’ve included the entire definition of
describe function in the
Item class) in the definition of
Weapon::describe, but this isn’t actually necessary; we can call
Item::describe directly from
It’s worth mentioning at this point the existence of
pure virtual functions; a
const function is not a function that returns a
const type (think about this for a second and you’ll see why this is mostly superfluous) but instead is one that doesn’t modify any member variables. A
pure virtual function forces an entire class to be defined as abstract, which means that it cannot be instantiated itself, although any derived classes can be. The syntax for these functions is a bit different
const function cannot modify any member variables, even by calling other member functions, they are restricted to being able to call other
const member functions. The pure virtual specifier can also only be included in function definitions and not declarations, although pure virtual functions can still be defined outside of the class body.
When first looking at classes we ran into the problem where a non-member function needed to access
private members of a class, but we didn’t want to give access to anything else. To solve this we introduced two
public getter functions, but there’s an alternative way using the
friend keyword. Returning to the
Rational class for this example,
By adding a declaration of a non-member function with
friend in front to class definition we grant access of all
private member variables to that function.
operator* can therefore be rewritten as if
Classes can also be defined to be
friends of other classes, using similar syntax. In this way, the
friend class can access all
private members of the other class. Two-way access is not granted automatically however,
friendship must be declared in both classes if
private members are to be accessible both ways.
C++ also has an additional access specifier,
protected, which restricts access of a class’ members to the members themselves and any
friends. It is not identical to
private though, as any derived class can also access the
protected members, whereas
private members are inaccessible to the derived class. Classes can also inherit using
protected, in which case all
protected members become
protected, and all
private members are inaccessible. The
protected access specifier therefore grants a way of restricting access to derived classes, but not the outside world. When using
protected members preserve their
protected nature or become
Sorry, there’s no exercise for this section!