17.x — Chapter 17 comprehensive quiz


Inheritance allows us to model an is-a relationship between two objects. The object being inherited from is called the parent class, base class, or superclass. The object doing the inheriting is called the child class, derived class, or subclass.

When a derived class inherits from a base class, the derived class acquires all of the members of the base class.

When a derived class is constructed, the base portion of the class is constructed first, and then the derived portion is constructed. In more detail:

  1. Memory for the derived class is set aside (enough for both the base and derived portions).
  2. The appropriate derived class constructor is called.
  3. The base class object is constructed first using the appropriate base class constructor. If no base class constructor is specified, the default constructor will be used.
  4. The initialization list of the derived class initializes members of the derived class.
  5. The body of the derived class constructor executes.
  6. Control is returned to the caller.

Destruction happens in the opposite order, from most-derived to most-base class.

C++ has 3 access specifiers: public, private, and protected. The protected access specifier allows the class the member belongs to, friends, and derived classes to access the protected member, but not the public.

Classes can inherit from another class publicly, privately, or protectedly. Classes almost always inherit publicly.

Here’s a table of all of the access specifier and inheritance types combinations:

Access specifier in base class Access specifier when inherited publicly Access specifier when inherited privately Access specifier when inherited protectedly
Public Public Private Protected
Private Inaccessible Inaccessible Inaccessible
Protected Protected Private Protected

Derived classes can add new functions, change the way functions that exist in the base class work in the derived class, change an inherited member’s access level, or hide functionality.

Multiple inheritance enables a derived class to inherit members from more than one parent. You should avoid multiple inheritance as much as possible.

Quiz time

Question #1

For each of the following programs, determine what they output, or if they would not compile, indicate why. This exercise is meant to be done by inspection, so do not compile these (otherwise the answers are trivial).


Show Solution

b) Hint: Local variables are destroyed in the opposite order of definition.

Show Solution


Show Solution


Show Solution


Show Solution

Question #2

a) Write an Apple class and a Banana class that are derived from a common Fruit class. Fruit should have two members: a name, and a color.

The following program should run:

And produce the result:

My apple is red.
My banana is yellow.

Show Solution

b) Add a new class to the previous program called GrannySmith that inherits from Apple.

The following program should run:

And produce the result:

My apple is red.
My banana is yellow.
My granny smith apple is green.

Show Solution

Question #3

Challenge time! The following quiz question is more difficult and lengthy. We're going to write a simple game where you fight monsters. The goal of the game is to collect as much gold as you can before you die or get to level 20.

Our program is going to consist of 3 classes: A Creature class, a Player class, and a Monster class. Player and Monster both inherit from Creature.

a) First create the Creature class. Creatures have 5 attributes: A name (std::string), a symbol (a char), an amount of health (int), the amount of damage they do per attack (int), and the amount of gold they are carrying (int). Implement these as class members. Write a full set of getters (a get function for each member). Add three other functions: void reduceHealth(int) reduces the Creature's health by an integer amount. bool isDead() returns true when the Creature's health is 0 or less. void addGold(int) adds gold to the Creature.

The following program should run:

And produce the result:

The orc has 3 health and is carrying 15 gold.

Show Solution

b) Now we're going to create the Player class. The Player class inherits from Creature. Player has one additional member, the player's level, which starts at 1. The player has a custom name (entered by the user), uses symbol '@', has 10 health, does 1 damage to start, and has no gold. Write a function called levelUp() that increases the player's level and damage by 1. Also write a getter for the level member. Finally, write a function called hasWon() that returns true if the player has reached level 20.

Write a new main() function that asks the user for their name and produces the output as follows:

Enter your name: Alex
Welcome, Alex.
You have 10 health and are carrying 0 gold.

Show Solution

c) Next up is the Monster class. Monster also inherits from Creature. Monsters have no non-inherited member variables.

First, write an empty Monster class inheriting from Creature, and then add an enum inside the Monster class named Type that contains enumerators for the 3 monsters that we'll have in this game: DRAGON, ORC, and SLIME (you'll also want a max_types enumerator, as that will come in handy in a bit).

Show Solution

d) Each Monster type will have a different name, symbol, starting health, gold, and damage. Here is a table of stats for each monster Type:

Type Name Symbol Health Damage Gold
dragon dragon D 20 4 100
orc orc o 4 2 25
slime slime s 1 1 10

Next step is to write a Monster constructor, so we can create monsters. The Monster constructor should take a Type enum as a parameter, and then create a Monster with the appropriate stats for that kind of monster.

There are a number of different ways to implement this (some better, some worse). However in this case, because all of our monster attributes are predefined (not random), we'll use a lookup table. A lookup table is an array that holds all of the predefined attributes. We can use the lookup table to look up the attributes for a given monster as needed.

So how do we implement this lookup table? It's not hard. We just need an array that contains an element for each monster Type. Each array element will contain a Creature that contains all of the predefined attribute values for that Type of Monster. We place this array inside of a static member function of Monster so that we can get a default Creature for a given Monster::Type.

The definition of the lookup table is as follows:

Now we can call this function to lookup any values we need! For example, to get a Dragon's gold, we can call getDefaultCreature(Type::dragon).getGold().

Use this function and delegating constructors to implement your Monster constructor.

The following program should compile:

and print:

A orc (o) was created.

Show Solution

e) Finally, add a static function to Monster named getRandomMonster(). This function should pick a random number from 0 to max_types-1 and return a monster (by value) with that Type (you'll need to static_cast the int to a Type to pass it to the Monster constructor).

Lesson 8.5 -- Random number generation contains code you can use to pick a random number.

The following main function should run:

The results of this program should be randomized.

Show Solution

f) We're finally set to write our game logic!

Here are the rules for the game:

The player encounters one randomly generated monster at a time.
For each monster, the player has two choices: (R)un or (F)ight.
If the player decides to Run, they have a 50% chance of escaping.
If the player escapes, they move to the next encounter with no ill effects.
If the player does not escape, the monster gets a free attack, and the player chooses their next action.
If the player chooses to fight, the player attacks first. The monster's health is reduced by the player's damage.
If the monster dies, the player takes any gold the monster is carrying. The player also levels up, increasing their level and damage by 1.
If the monster does not die, the monster attacks the player back. The player's health is reduced by the monster's damage.
The game ends when the player has died (loss) or reached level 20 (win)
If the player dies, the game should tell the player what level they were and how much gold they had.
If the player wins, the game should tell the player they won, and how much gold they had
Here's a sample game session:

Enter your name: Alex
Welcome, Alex
You have encountered a slime (s).
(R)un or (F)ight: f
You hit the slime for 1 damage.
You killed the slime.
You are now level 2.
You found 10 gold.
You have encountered a dragon (D).
(R)un or (F)ight: r
You failed to flee.
The dragon hit you for 4 damage.
(R)un or (F)ight: r
You successfully fled.
You have encountered a orc (o).
(R)un or (F)ight: f
You hit the orc for 2 damage.
The orc hit you for 2 damage.
(R)un or (F)ight: f
You hit the orc for 2 damage.
You killed the orc.
You are now level 3.
You found 25 gold.
You have encountered a dragon (D).
(R)un or (F)ight: r
You failed to flee.
The dragon hit you for 4 damage.
You died at level 3 and with 35 gold.
Too bad you can't take it with you!
Hint: Create 4 functions:

The main() function should handle game setup (creating the Player) and the main game loop.
fightMonster() handles the fight between the Player and a single Monster, including asking the player what they want to do, handling the run or fight cases.
attackMonster() handles the player attacking the monster, including leveling up.
attackPlayer() handles the monster attacking the player.

Show Solution

g) Extra credit:
Reader Tom didn't sharpen his sword enough to defeat the mighty dragon. Help him by implementing the following potions in different sizes:

Type Effect (Small) Effect (Medium) Effect (Large)
Health +2 Health +2 Health +5 Health
Strength +1 Damage +1 Damage +1 Damage
Poison -1 Health -1 Health -1 Health

Feel free to get creative and add more potions or change their effects!

The player has a 30% chance of finding a potion after every won fight and has the choice between drinking or not drinking it. If the player doesn't drink the potion, it disappears. The player doesn't know what type of potion was found until the player drinks it, at which point the type and size of the potion is revealed and the effect is applied.

In the following example, the player found a poison potion and died from drinking it (Poison was much more damaging in this example)

You have encountered a slime (s).
(R)un or (F)ight: f
You hit the slime for 1 damage.
You killed the slime.
You are now level 2.
You found 10 gold.
You found a mythical potion! Do you want to drink it? [y/n]: y
You drank a Medium potion of Poison
You died at level 2 and with 10 gold.
Too bad you can't take it with you!

Show Hint

Show Solution

18.1 -- Pointers and references to the base class of derived objects
17.9 -- Multiple inheritance

436 comments to 17.x — Chapter 17 comprehensive quiz

  • yeokaiwei

    Why do we derive Player from Creature?

    Why do we not use Player as a Base Class?

    • Alex

      So that the attributes and behaviors common to both Player and Monster can be put in Creature, and those that differ can be put in either Player or Monster respectively.

      • yeokaiwei

        Hi Alex,

        Is there a methodology for how many levels of abstraction?

        How should I break down my classes?

        Is it like the blackjack tutorial where there 4 types of logic?

        1. Player Archetypes
        class Barbarian : public Player

        Why do I not create a Barbarian class as a Base?

        Why derive abilities from another class, Player?

        The answer someone told me was that Player and Monster had a different logic layer.

        Extending from that, I guess it meant that I could also combine

        class Barbarian : public Monster

        and use Monster AI to control the Barbarian.

        2. Centaur Diamond Problem
        If I have a Horse Class and Human Class, how should I arrange a Centaur?

        I want to derive the movement functions from a Horse but I want the Human speak().

        Is a Centaur a case of multiple inheritance?

        3. ??? Issue
        Lastly, I encountered an issue with looking up Base class member functions.

        While I was looking in the Player class, I can't see Creature class member functions e.g. getsomething()

        This means I have to guess if there's such a function and keep scrolling up and down whenever I'm unsure if Creature has a certain base member function.  

        One solution was to use "virtual" specifier in Creature and copy-paste the code into Player. However, this solution seems to defeat the purpose of using a Derived class in the first place.

        Is there a method/trick that you use during your coding to solve scroll up, scroll down issue?

        I do not know what is the correct adjective for this type of issue. Dependency issue? Derivation issue? Ergonomic issue?

  • yeokaiwei

    1. Compliments & Feedback on Quiz 3

    "8.x — Chapter 8 summary and quiz | Learn C++
    21 Dec 2020 — The monster should have a type that can be one of the following: an ogre, a dragon, an orc, a giant spider, or a slime. Use an enum class for ..."

    6.x — Chapter 6 summary and quiz | Learn C++
    9 May 2015 — void printMonster(MonsterStats slime, MonsterStats spider, MonsterStats orc, MonsterStats ogre, MonsterStats dragon, int charLevel)

    17.x — Chapter 17 comprehensive quiz | Learn C++
    29 Oct 2016 — For example, to get a Dragon's gold, we can call getDefaultCreature(Type::dragon).getGold()."

    Dear Alex,
    It's the end of 2020.

    Thank you for the hard work in maintaining

    It must be hard to maintain the tutorial examples.

    If you segregate them by chapter, the difference in updates vary by years ie 2015, 2016, 2020

    Perhaps, you would like to create a super example page?

    In this case, you could call it the Dragon, Orc, Slime (DOS) Game.

    With a single super example, you could link the parts of the code to Solution and you would only need to update the Super Example.

    These are the benefits off the top of my head.
    1. Updated and consistent across the pages
    2. No major differences between pages
    3. With a super example, you can always pair it with a concept.
    E.g. The Dragon is about teaching Inheritance, due to the Dragon being on a hoard of gold in stories.
    The Slimes are about Pseudo Random Number Generation, Red slime, blue slime, green slime, pink slime, etc
    The Orcs are about evil nasty code. Like Global Variables, Coercion.
    The Wizard is about casting. They "cast"
    The Giant Spider is about bugs. Technically, they are arachnids.

    Each time a new monster is added to the code, you are teaching a new concept. Then, a teaching pattern will exist. So, if I want to remember about how to do casting? I will think, What class/creature casts? A wizard must cast. So, I look for the Wizard example.

    If I want "evil code", I think of the Orc.

    If I want buggy code, I think of the Giant Spider.

  • yeokaiwei

    1. Feedback

    In Microsoft Visual Studio 2019, the compiler will return
    C2662 : 'bool Creature::isDead(void)' Cannot convert 'this' pointer from 'const Monster" to 'Creature &'
    E1086 the object has type qualifiers that are not compatible with the member function "Creature::isDead()"

    To solve it, one just needs to remove the const(read-only).

    • nascardriver

      You need to mark `isDead` as `const`. A member function that doesn't modify anything should be `const`. Reference parameters that don't get modified should be `const`.

  • yeokaiwei

    I did the Quiz 3 somewhat differently from the tutorial.

    I hope this is acceptable.

    //1. Player names cannot take in whitespace
    //2. E0070 incomplete type is not allowed.
    //An incomplete type is a type that describes an identifier
    //but lacks information needed to determine the size of the identifier.
    //An incomplete type can be:
    //An array/struct/union type whose dimension you have not yet specified.
    //Simply due to no #include <array>
    //3. Monster m{ Monster::MonsterType::orc };
    //C2440 'initializing': cannot convert from 'initializer list' to 'Monster'
    //This is due to the "&".
    //Monster(Monster::MonsterType& monstertype): Creature{ getDefaultCreature(monstertype)} {}
    //Remove the "&" in the constructor parameters and it works.
    //The debugger is extremely unhelpful for C2440.
    //4. C4100 'monstertype': unreferenced formal parameter
    //Forgot ": Creature{getDefaultCreature(monstertype)}". Unfamiliar with putting it in the {}.
    //5.    m_level += 1;
    //        m_dmg += 1;
    //Just use ++m_level and ++m_dmg if it's 1
    //6. E0245 a nonstatic member reference must be relative to a specific object
    //Monster getRandomMonster()
    //    std::mt19937 mersenne{ static_cast<std::mt19937::result_type>(std::time(nullptr)) };
    //    std::uniform_int_distribution die{ 0, static_cast<int>(Monster::MonsterType::max_types) - 1 };//Static cast the enum to int
    //    return static_cast<Monster::MonsterType>(die(mersenne));//Static cast the int to enum
    //Just add "static", static Monster getRandomMonster().
    //However, you get the same random answer. This is a common mistake.
    //7. C6031 Return value ignored: 'rand'
    //This is due to escape from zeroland.
    //The program will still compile. This is not an issue.
    //8. Level reset loop 1>2>1>2>1>2
    //Beware of a player level reset loop when you input the parameters in a normal/outside function instead of a member function.
    //levelUp() is a member function
    //It may have something to do with judicious use of "static".
    //printStats() is extremely useful for debugging.
    //I hope this could be added to the tutorial as I've noticed that some other users couldn't defeat the dragon.
    //9. When you have an Encounter/fightMonster(), different loops endings are required for running and fighting.
    //For running, you want to have a new Encounter with a new monster, regardless of escape result.
    //For fighting, you don't want a new Encounter until you deal with the monster.
    //Continue, Break, Return (CBR)
    //Their uses come in a set and it helps a lot in dealing with loops.
    //CBR could be taught as an acronym set.
    //10. The release .exe x64 file crashes after a certain number of steps. I am unsure of the reason.***
    //11. To solve the extra credit part, I added an Item base Class and a Potion derived Class. This was wrong when I checked the solution.
    //12. To avoid the enum to std::string switch hassle or overloading. I used the a?b:c method to print out the sizes instead. This method has some advantages as I do not need to update a enum-string conversion function in the future. It just prints out an additional line of text.

  • yeokaiwei

    1. Feedback on Typo
    "... static member function the creates a random Potion, similar to the getRandomMonster() function."

    that creates

  • yeokaiwei

    Partial solution.

    Yet to factorise.

  • yeokaiwei

    1. Quiz 2
    I didn't do it with the "const" keyword but it compiled.

    I hope this is acceptable.

  • qwerty

    Hi why is it necessary to use static for the array ?

  • SaMiRa

    I applied all the improvement you mention on the last posts for the question #3 part f and I solved the quiz question #3 part g--Extra Credit.
    Would you please check that for me?

    P.S: I decided to write this part to be used for any values for the potion table. For example, I used the following values:

    Health    2,   3,   5
    Strength  1,   2,   1
    Poison   -1,  -2,  -3

    P.S: I used a different way to calculate that 0 chance of finding potion. I think I could be completely wrong.