Search

11.x — Chapter 11 comprehensive quiz

Summary

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

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).

1a)

Show Solution

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

Show Solution

1c)

Show Solution

1d)

Show Solution

1e)

Show Solution

2a) 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

2b) 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

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.

3a) 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

3b) 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

3c) 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

3d) 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 two things. First, we need an array that contains an element for each monster Type. Each array element will contain a struct that contains all of the predefined attribute values for that Type of Monster.

Since all the properties of monsters are known at compile-time, we can declare make the lookup a static constexpr member of Monster. The definition of the lookup table is as follows:

A reminder

If you get an error in the declaration of monsterData, make sure your compiler is C++17 capable by trying to compiling the example in lesson 0.12 -- Configuring your compiler: Choosing a language standard.

Now we can index this array to lookup any values we need! For example, to get a Dragon's gold, we can access monsterData[Type::DRAGON].gold.

Use this lookup table to implement your constructor:

The following program should compile:

and print:

A orc (o) was created.

Show Solution

3e) 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 5.9 -- 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

3f) 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:

  1. The main() function should handle game setup (creating the Player) and the main game loop.
  2. 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.
  3. attackMonster() handles the player attacking the monster, including leveling up.
  4. attackPlayer() handles the monster attacking the player.

Show Solution


12.1 -- Pointers and references to the base class of derived objects
Index
11.7 -- Multiple inheritance

360 comments to 11.x — Chapter 11 comprehensive quiz

  • salah

    Hi,
    why 'Cat cat1 {cat}' would call Animal constructor??
    It's obvious why 'Cat cat {"aa"}' calls the Animal constructor. But what about 'Cat cat1 {cat}' ? should not it call just the copy constructor of 'Cat'?

    Result:

    Animal constructor
    cat constructor
    Animal constructor
    cat copy constructor
    aa

  • robinchaz

    Hello for question 3d,line 87, the data() member function is part of the string_view class and it returns a pointer to the underlying array, so is the argument passed to Creature constructor const std::string &name=pointer?
    If my above question is unclear, my question is what arguments does the const std::string &name parameter take? and also for e.g const std::string& str="Hello"; ,is the string literal converted to a anonymous std::string object and str is a const ref to it?

  • Connor

    Hello! This is my attempt at the final quiz question. Hope it's all good, took me quite a while cause I mucked up the first time (it was an absolute mess). Thanks and have a nice day! :)

    // main.cpp

    // Creature.h

    // Creature.cpp

    // Game.h

    // Game.cpp

    // Helper.h

    // Helper.cpp

    // Monster.h

    // Monster.cpp

    // Player.h

    • nascardriver

      Even got some `noexcept` in there, nice :)

      - Include as few files as possible in headers. More includes means slower compilation and higher risk of circular dependencies.
      - Initialize variables with list initialization for higher type-safety.
      - There's no need to explicitly default `Game::Game`. It's implicitly-declared, because you have no other constructors.
      - Only use pointers if `nullptr` is handled. Your functions cause undefined behavior when a `nullptr` is passed to them. Use references instead.
      - `Game::start` would've benefited from being separated into more functions. This would also spare you the `break`, which could be replaced by `return` (Which is easier to understand, because the reader doesn't have to search for the end of the loop).
      - `std::system` is platform-specific, your program won't behave well on anything other than Windows. If you have to use ugly code, hide it inside function to make porting the code easier. eg. `clearScreen`, `pause`, and so on, then call those functions instead of using `std::system` directly.
      - Magic values: 20, 'r', 'f'
      - If you're using a standard function without the `std::` prefix, you're using a C function. In C++, everything was moved into the `std` namespace.
      - It's been found now :)
      - Strings are more expensive than characters. Use single quotes for characters.
      - You never need

      These can and should always be returned with

      Pretty nice code overall, keep it up!

      • Connor

        Thanks for the advice & tips ! Will be sure to implement them into the code and future code I write from now on, apart from the uniform/list initialization for variables (just hate the way it looks lol).

        About the usage of std::system(), I am still yet to find a platform independent function that will let me change text colour inside of the console. Would I be best just using a 3rd-party library for doing it, that deals with all of the platform specific details? Thanks!

  • slimeyzilla

    Getting some very strange errors. Here is my code for the Creature and Monster classes:

    When attempting to build this program, I receive the following errors:

    If I remove 'static const' from the line in which the array is declared, my compiler tells me that there is an unhandled exception in the Creature constructor... (???)

    • nascardriver

      You cannot initialize `static` members inside the class unless they're const and have an integral type or are constexpr. `std::array` isn't an integral type, so you can't initialize it in the class with `const`. You could initialize it in the class if it were `constexpr`, but you use `std::string` to store the name and `std::string` can't be `constexpr`.
      If you use `std::string_view` for the name and make `monsterData` `constexpr`, your code works. If you're using an old compiler, you can't do that. In that case, initialize `monsterData` outside of the class.

      • Slimeyzilla

        I just read up on this, and it seems to simply be a rule in C++ to prevent multiple declarations/definitions of a single variable, particularly in the case that it is const and cannot be altered after-the-fact.

        The main issue, it seems, is that if I remove ``const`` from the following code segment, I will be presented with the error that in-class initializers must be ``const``, but if I make the std::array static, the compiler will complain that the static member is either not inline, or is not an integral type / constant enumerated type. I can see that using ``constexpr`` with std::string is not possible, because std::string is not a literal type.

        Does this mean that this array can only be implemented using constexpr, and using a literal implementation of strings, such as std::string_view?
        Also, thank you for the help!

        • Slimeyzilla

          Thank you nascardriver! You are truly diligent, helping all of these people for so long, with such consistency! I hope to be as great as you guys some day!

        • nascardriver

          A type that has a member variable of non-literal type, is a non-literal type. So your `MondayData` cannot be `constexpr`. If you remove the `std::string` and replace it with `std::string_view` (Which is a literal type), your entire `MonotoneData` becomes a literal type as well, so you can use it with `constexpr`, and `constexpr` member variables can be initialized inside the class.

  • Mn3m

    3f)
    Things got a little bit messy...
    I hope you can provide some feedback.
    Also, I have a few question..

    1 - When you said "escape without ill effects", you meant escaping without getting hit, not restoring player's health right?

    2 - At the beginning of the source code, I tried to forward declare the global functions, such as fightMonster(), but I got an error that says "illegal use of void". What's the issue here?

    3 - Regarding the chance of fleeing... I reused the getRandomNumber() function to set a flag's value (either 1: success, 0: fail). Does that compensate for having to do some math/statistics maybe?

    • nascardriver

      1 - I didn't write this quiz, but I think that's what it means.

      2 - That's an odd error message. You need to forward declare `Player` and `Monster` before you can use them in forward declarations of the functions.

      3 - Nothing wrong with that. If you have a function, you should use it.

      - Returning by `const` value has no benefit but prevents fast returns.
      - `isDead` and `hasWon` still aren't `const`. That's what the function in my last reply should've shown you.
      - Line 118, 189, 190,
      - Don't seed random number generators more than once. You're not generating random numbers.
      - Lack of `const` on function parameters.
      - Line 219 is misleading. Are you using an auto-formatter?

      • Mn3m

        Thanks for the feedback, the second point is interesting though... I don't know if this error was pointed out in previous lessons. It's the first time I encounter it. Maybe you guys can add it as a side note if it's not there already (just a suggestion :$).

        I clearly need to pay more attention to 'const' for parameters, I keep forgetting to include it.

        Well, VS automatically adjust things when I type  ';', but that 'break' is misleading because of my spacing probably. It's for the case 'r'.

  • Mn3m

    3b)

    • nascardriver

      `player` should be initialized with list initialization.

      Why doesn't this work?

      • Mn3m

        I really didn't understand what you were trying to say here :$...

        Was this reply meant for me? Because this is still 3b.

        • nascardriver

          Yes, all as intended. If you try to add the function I posted to your code, it won't work. I could tell you why it doesn't work, but if you figure it out yourself, you'll remember better.

  • Mohammad

    Why can't I write the following code for the quiz 2(b)?

    It flags an error in the line 45.
    Sorry to disturb you, but advanced thanks.

    • nascardriver

      Line 45 tries to invoke the default constructor, but `GrannySmith` has no default constructor. You have to pass it a name.

  • Lasha

    I am referencing the last solution to the last problem (3f).

    On line 88, there is a code:

    You mentioned that monsterData has to be static because in the constructor that variable will not exist yet. Now, 'monsterData' is created when the program starts, but the array has the length of Monster::MAX_TYPES, which is enum inside a class.

    Does that mean, that 'enum Type' acts as a static member variable because it was accessed without creating any object of the class?

    The same goes for 'struct MonsterData', it's used as an argument for array type without any errors.

  • Parsa

    Why do we need an extra set of curly braces here?

    • nascardriver

      See section "Array of struct" in lesson 6.15 https://www.learncpp.com/cpp-tutorial/6-15-an-introduction-to-stdarray/#struct

  • choofe

    Hi
    I have written a function to distribute the chance of appearing an outcome to be customized. It may sounds useless and there may be better such things but I came up with it myself. And want your opinion on that. Because it seems a little inconsistent.I used it instead of getRandomNumber(). My approach to last question is also a bit different.I really can use your comment.

    • nascardriver

      Hi!

      - Name variables descriptively. Abbreviations don't provide enough information to make your code easy to read.
      - Pass/return non-fundamental types by const reference. Copying them is expensive. (`Creature::getName`, `operator<<(std::ostream&, Monster&)`, some more missing `const`) - Line 126: Good comment. For documentations, you can use documentation comments