C++ Scope when using Inheritance

Learn how the scoping rules apply when dealing with inheritance, and how we can bypass them with the Scope Resolution operator
This lesson is part of the course:

Intro to C++ Programming

Become a software engineer with C++. Starting from the basics, we guide you step by step along the way

3D art of a man using a telescope
Ryan McCombe
Ryan McCombe
Posted

We've already seen how our classes can inherit functions and variables from their ancestors. A common requirement is that child classes be able to change the things they're inheriting.

Lets imagine we have the following classes:

class Character {
public:
  int GetHealth() {
    return Health;
  };
  int Health { 100 };
};

class Monster : public Character {
public:
  int Health { 200 };
};

Our goal is that both our Character and more specific Monster objects to have a Health property, but for their value to be different.

Lets create a Monster and see if our code works:

Monster SomeGoblin;
cout << SomeGoblin.Health << endl;
cout << SomeGoblin.GetHealth();

Our output is:

200
100

This is not quite what we want. SomeGoblin.Health is returning the Health value from our Monster class, but GetHealth() is using the value from our Character class.

Understanding Scope with Inheritance

This behaviour is quite similar to what we previously saw in our introductory lesson on scopes. There, we saw when we refer to a variable, the most local version of that variable is used.

Just like we saw in that lesson, the problem is that Health in the Character class and Health in the Monster class are different variables. They just happen to have the same name. We're "shadowing" or "masking" the variable, not replacing it.

Since SomeGoblin is a Monster, when we create an expression like SomeGoblin.Health, we look for the Health variable within the Monster class. That variable exists, so it is used.

However, when we have the expression SomeGoblin.GetHealth(), there is no GetHealth function in our Monster class, so the compiler searches up the inheritance tree.

It finds GetHealth exists on the parent scope, so it uses that function.

However, now that we are in the scope of the Character class, we no longer have access to things that are in the child scope. The Health variable of the Monster class is out of reach, so the GetHealth function returns the value stored in the Character class.

Test your Knowledge

After running the following code, what is the value of LegCount?

class Animal {
public:
  int CountLegs() { return 4; }
};

class Spider : public Animal {
public:
  int CountLegs() { return 8; }
};

Animal Daisy;
int LegCount { Daisy.CountLegs() };

After running the following code, what is the value of LegCount?

class Animal {
public:
  int CountLegs() { return 4; }
};

class Spider : public Animal {
public:
  int CountLegs() { return 8; }
};

Spider Spooky;
int LegCount { Spooky.CountLegs() };

After running the following code, what is the value of LegCount?

class Animal {
public:
  int CountLegs() { return 4; }
};

class Spider : public Animal {
public:
  int CountLegs() { return 8; }
};

class Tarantula : public Spider {};

Tarantula Hairy;
int LegCount { Hairy.CountLegs() };

After running the following code, what is the value of LegCount?

class Animal {
public:
  int CountLegs() { return LegCount; }
  int LegCount { 4 };
};

class Spider : public Animal {
public:
  int LegCount { 8 };
};

Spider Spooky;
int LegCount { Spooky.CountLegs() };

After running the following code, what is the value of LegCount?

class Animal {
public:
  int CountLegs() { return LegCount; }
};

class Spider : public Animal {
public:
  int LegCount { 8 };
};

Spider Spooky;
int LegCount { Spooky.CountLegs() };

Fixing C++ Scope Issues

So, how can we solve this simple problem of letting our Monster objects have a different Health value? One option is duplicating the GetHealth function into the class:

class Character {
public:
  int GetHealth() {
    return Health;
  };
  int Health { 100 };
};

class Monster : public Character {
public:
  int GetHealth() {
    return Health;
  };
  int Health { 200 };
};

But this loses all the advantages of inheritance that we talked about. A better option would be to update the inherited value from the default constructor:

class Character {
public:
  int GetHealth() {
    return Health;
  };
protected:
  int Health { 100 };
};

class Monster : public Character {
public:
  Monster() {
    Health = 200;
  };
};

We've additionally moved the Health variable into the protected section. This is not necessary, but is a good practice, as described in our lessons on encapsulation.

Now, our inherited class works as we expect:

Monster SomeGoblin;
cout << SomeGoblin.GetHealth();

Our output is:

200

Extending Base Functions and the :: operator

Another situation we're likely to run in to is one where we need to replace an inherited function:

class Character {
public:
  void TakeDamage() {
    cout << "I have taken damage" << endl;
  };
};

class Monster : public Character {
public:
  void TakeDamage() {
    cout << "You will pay for that!" << endl;
  };
};

This behaves as we might expect:

Monster SomeGoblin;
SomeGoblin.TakeDamage();
You will pay for that!

This is straightforward when the function we want to replace is very simple.

However, that will not always be the case. The TakeDamage function might have lots of moving parts and, from our inherited function, we still want to do all of those things. We just want to do something extra, too.

Rather than duplicating the entire function, we can instead call the base class function from our inherited class function. However, given the functions have the same name, it's not too clear how to do that.

Doing it directly won't work - we'll just create an infinite loop:

class Monster : public Character {
public:
  void TakeDamage() {
    TakeDamage();
    cout << "You will pay for that!" << endl;
  };
};

Fortunately, we have the Scope Resolution operator, ::.

Where there are multiple functions or variables with the same name available within a scope, we can use the :: operator to specify which one we want to use. This lets us bypass the default "use the most local scope" behaviour when we try to access a variable or function.

class Character {
public:
  void TakeDamage() {
    cout << "I have taken damage" << endl;
    // Imagine lots of additional code here
  };
};

class Monster : public Character {
public:
  void TakeDamage() {
    Character::TakeDamage();
    cout << "You will pay for that!" << endl;
  };
};

Now, we have unlocked the ability to extend inherited functions, rather than just replacing them entirely.

Monster SomeGoblin;
SomeGoblin.TakeDamage();
I have taken damage
You will pay for that!

Test your Knowledge

After running the following code, what is the value of LegCount?

class Animal {
public:
  int LegCount { 4 };
};

class Spider : public Animal {
public:
  int CountLegs() {
     return Animal::LegCount;
  }
  int LegCount { 8 };
};

Spider Spooky;
int LegCount { Spooky.CountLegs() };

C++ Super doesn't exist (*)

Many other object oriented language allow developers to use the Super keyword to refer to the parent class.

This has the benefit where, if our class is updated to inherit from a different base class (sometimes referred to as "reparanting"), the Super keyword will automatically reflect that.

This is not a feature in C++, because C++ supports multiple inheritance. We'll cover this in more detail in the intermediate course, but the summary is that it's possible for a class to have multiple base classes. So, it's not clear which class Super would refer to.

* It Sometimes Exists

However, in some environments, multiple inheritance is restricted, and Super is available.

This includes C++ code written for Unreal Engine, which is passed through a custom preprocessor (the Unreal Header Tool) before it is sent to the compiler.

One thing that preprocessor does is allow us to use Super:

// This only works if our code is handled by Unreal
class MyClass : public SomeUnrealClass {
  void TakeDamage() {
    Super::TakeDamage();
    cout << "You will pay for that!";
  };
}

Was this lesson useful?

Ryan McCombe
Ryan McCombe
Posted
This lesson is part of the course:

Intro to C++ Programming

Become a software engineer with C++. Starting from the basics, we guide you step by step along the way

Inheritance
3D art showing a progammer setting up a development environment
This lesson is part of the course:

Intro to C++ Programming

Become a software engineer with C++. Starting from the basics, we guide you step by step along the way

Free, unlimited access!

This course includes:

  • 66 Lessons
  • Over 200 Quiz Questions
  • Capstone Project
  • Regularly Updated
  • Help and FAQ
Next Lesson

C++ References - Why We Need Them

A deeper look at what is happening when we pass arguments to functions, and an introduction to the problem that references are designed to solve.
3D art showing a female blacksmith character
Contact|Privacy Policy|Terms of Use
Copyright © 2023 - All Rights Reserved