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

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.

Step 1: Create a struct type inside Monster named MonsterData. This struct should have a member for each attribute (name, symbol, health, damage, and gold).
Step 2: Declare an array of that struct as a static member of the class named monsterData (declare this like a normal array member, and then add the word static before it).
Step 3: Add the following code outside of the class. This is the definition for our lookup table:

Now we can index this array to lookup any values we need! For example, to get a Dragon's gold, we can access monsterData[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 between 0 and 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).

You can use the following code 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 will 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.
The orc hit you for 2 damage.
(R)un or (F)ight: f
You hit the orc for 2 damage.
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

97 comments to 11.x — Chapter 11 comprehensive quiz

  • Omri

    Got it.Thanks.
    These are class s_variables as opposed to m_variables, and since they globally pertain to all objects of the class, and can even be globally accessed using only the class_name:: prefix ( no object required) they are "logically" defined in the global space.
    For their association with the class in the "static manner", they must be declared as static within the class definition.
    Is this correct?

  • Omri

    I tried to do the following *within* the Monster class definition block (to save typing…) and it did not compile:
    static MonsterData monsterData[Monster::MAX_TYPES]
    { { "dragon", ‘D’, 20, 4, 100 },
        { "orc", ‘o’, 4, 2, 25 },
        { "slime", ‘s’, 1, 1, 10 } };
    The compiler complained about the combination of static and initialization within the class definition (as I recall…).
    When I did it "your way":
    A. array declaration - including static - inside the class definition block.
    B. array initialization outside the class definition block with Monster:: preceding both MonsterData and monsterData.
    it compiled fine and all went well.
    Whats wrong with the first version?

    • Alex

      Static members aren’t actually part of the class, so you’re not able to define them inside the class(with an exception made for const int and const enums since the compiler can optimize these away anyway).

      This is covered in lesson http://www.learncpp.com/cpp-tutorial/811-static-member-variables/

  • Omri

    Relating to:
    "Step 2: Declare an array of that struct as a static member of the class named monsterData (define this like a normal array member, and then add the word static before it)."
    Consider:
    "Step 2: Declare an array of that struct as a static member of the class named monsterData (*declare* this like a normal array member, and then add the word static before it)."

  • C++ Learner

    What means "The appropriate derived class constructor is called."
    I think that parent class constructor is called and then derived?
    Can you explain why is it like that?

    Memory for the derived class is set aside (enough for both the base and derived portions).
    The appropriate derived class constructor is called.
    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.
    The initialization list of the derived class initializes members of the derived class.
    The body of the derived class constructor executes.
    Control is returned to the caller.

  • Seamus Callaghan

    The game was practically impossible to win, so I balanced it a little.  I gave the player 11 health instead of 10, and upped the odds of escape from 1/2 to 2/3.  I still usually die, but I do now win occasionally.

  • Under the "The following program should compile:" in Question 3-D, the main function has an extra bracket.
    Thanks for the tutorials as always.

  • Johnbosco

    please how does the following lines of code execute

    1. From my understanding monsterData[Monster::MAX_TYPES] is an array of type MonsterData.
    2. Does it mean { "dragon", ‘D’, 20, 4, 100 } is in index 0 in our array and { "orc", ‘o’, 6, 2, 25 } at index 1?
    3. did we initialize our struct and our array at the same time?
    Thanks for the tutorial

  • Deniz

    Hi Alex,
    while attempting to solve the exercise i had an idea to use a pointer to a function in a function as a small logic to choose whether the player or the monster attack(i realize this is probably an necessary complication, but its practice). However, when i try to implement this in much the same way as you did in previous lectures(defining a function pointer form the library with appropriate return and input types, and using assignment operator), i get an error, telling me that there is no overloaded = that can handle this assignment. Might this be due to the fact that the arguments of the functions i am trying to assign to a pointer are custom classes. How should i go about this?

    • Alex

      Can you show me what you tried? Both the function parameter definition and how you tried to call it.

      • Deniz

        Also, if i just define a normal pointer to a bool(Monster,Player) function in the main and again just assign it, it doesn’t work for the same reason, so i guess i have to write a = operator that can handle this? i know that there are many redundancies in the code here, but i actually gave up on the pointer thing and just finished the problem in a different way, so i just modified parts of it so the pointer fits. Nonetheless the idea comes across, i think. Also notice a static pointer in the code. It is there since didn’t want to pass the pointer to a function at all, rather just have a static, all purpose one since i need just one pointer. But then i figured that i might have misused this in some way and decided to just pass a pointer to the function and work form there. Neither of the approaches worked for me, obviously, so, help!?
        P.S The classes are implemented identically as in the solution so no point in putting it here.

        • Alex

          You’re close.

          Since function attackMonster has signature: bool attackMonster(Monster &a,Player &b)

          Your HowDoIDoThis function parameter should have type: bool HowDoIDoThis(Monster a, Player b, std::function placehold)

          Note the addition of the ampersands inside the std::function parameters to denote that these are reference parameters.

  • Ivan

    I have a question on "Creature" class method "getName":

    Why m_name is the only variable which is returned by reference ? What is the point of it ? It is not class or large array, what is the point of returning the m_name by reference ?

    • Alex

      std::string is a class, so returning by reference returns a reference to the actual Creature member rather than making a (potentially expensive) copy.

      • Ivan

        I have read all of your lessons till this one. And i perfectly understand how expensive could be returning some large structs or arrays by value. But in this case it is only a small m_name variable. Why aren’t we returning m_damage also by reference then ? 🙂
        Sorry for stupid question, it seems that i’m missing something.

        • Alex

          Non-class data types can be returned by value. Since m_damage is an int, it’s fine to return it by value.

          To recap:
          * Pass/return class objects by const/non-const reference to avoid making an expensive copy
          * Pass/return fundamental variables that do not need to be modified by value
          * Pass/return fundamental variables that need to be modified by reference

  • Miko

    Hi.

    I have this class. And when I change(or remove) the default value and run program it doesn’t update the value. I’m using visual studio 2015. Is there some simple way to make it to update value when program is run. One way was to remove default value in header file, assign value for it in constructor, run once, remove assign value from constructor, then add wanted default value in header file, but I don’t think that’s good solution.  

    • Alex

      There’s nothing wrong with this code. If you update the 1 to something else and recompile, the default value should be updated to whatever you changed it to. That means something else is wrong -- either you’re not recompiling after making the change, you’re compiling something other than what you’re expecting, or there’s an error elsewhere in your code.

      • Miko

        Can I some how force it to recompile files, I thought that when I click "(the green arrow) Local Windows Debugger" it would automaticly recompile like expected. It’s a bit hard to tell with words, I’ll post code to somewhere and also post short video of what’s happening somewhere to show what I mean. If you are willing to help, since I think this isn’t c++ problem but a visual studio problem.

        I think I was asking is this a commonly know problem in visual studio or is it just me.(but I used the wrong words)

        Also the header file and levelUp() {++m_level} function are only places that modify the value (Checked with "Find all referances" tool in visual studio)

        • Alex

          I don’t think the green arrow causes a recompile, it just runs your program in the debugger. To compile, after making a code change, go to the “Build” menu and choose “Build solution”. You can also hit ctrl-shift-B.

          • Miko

            Normally green arrow has build and run.

            Tried "Build solution" build was succes but it didn’t work, but "Rebuild solution" worked. Thank you.
            I think I had this kind of problem in some earlier task too(I didn’t post about it) which got fixed itself. Or so it seemed. I think I actually changed code some where enought to make it re-build "with green arrow"(XD), then removed  changes because it didn’t work, making it now re-build earlier version with correct value which worked.

            If I’m right about this problem and it isnt’ just me this would be good to add in somehwere that you have use re-build in visual studio 2015 if you are initializing class memebers that way in header.

            Also gave same problem as int m_level = 1;

  • Gapo

    Task 3b) :
    Could I also make it like this ?

    • Alex

      You _can_, but whether you _should_ is a different question.

      Generally constructors are used for initializing objects, not asking the user for input or printing things. If you need user input, it’s generally better to do it prior to the constructor and then pass those values into the constructor. Similarly, if you want to print something, it’s generally better to construct the object and then call a separate function to print, rather than do both in the constructor.

      • Gapo

        I understand, thank you 😀 Now I changed it to this :

        Trying to playaround a little, I am not sure how good of a practice it is to use the same object as argument upon its creation

        • Alex

          I’m surprised this compiles. You should do something like this instead:

  • Jacco

    Hi Alex,

    Why do you declare the monsterData array inside the class and further define it (fill it) outside? Could you not have defined the whole array inside Monster?

    • Alex

      We define monsterData inside the class as a way to restrict its use to this class. Remember that you should always define your variables in as small a scope as possible. Since this array is only used by the Monster class, it makes sense to declare it inside the Monster class.

      However, because it is a static member, the definition needs to be done outside the class definition. That’s one of those weird hoops that C++ makes you jump through when you use static members.

  • skugz

    For some reason my game won’t compile.
    I’m on stage 3c and I keep getting this message: "Creature::get.Name" non standard syntax; Use & to create a pointer to member.
    I ran your code too just to be sure I didn’t screw anything up and it does this for yours too.
    It’s not so much about the game but I’d like to know do new versions of VisualStudio make this impossible or is it something else?

    Loving these tutorials btw.

    Thanks.

    • Alex

      I’m not getting an error with Visual Studio 2017. What line of code is it actually complaining about?

      • skugz

        Here’s my code(since I’m a beginner I’m trying to get used to using comments):

        • Alex

          This line is incorrect:

          Remember that in order to call a function, you need to use parenthesis. The corrected line should look like this:

          cout << "\n"<< "Welcome " << player.getName() << ", you have " << player.getHealth() << " health and are carring " << player.getGold() << " gold" << endl;

          • skugz

            Thanks, I thought because it was saying something about members that they were for some reason unreachable so I didn’t even check the line.

  • Chris

    Hi Alex. For question 3, I decided to add custom functions for death messages, win messages, and custom loot collection. However, collecting some loot early on basically makes the game impossible to lose, as you eventually gain enough damage to kill the orc and the slime in one hit, and the dragon in two-three.

    I was wondering if there would be a way to make the monsters increase their health and damage as the player levels up, or if certain monsters would be unlocked and others would become locked depending on the player’s level. Which one would make more sense to do, and how exactly might I go about this?

    Thanks!

    • Alex

      Yes, as far as games go, this isn’t a great one. 🙂 Probably the easiest thing to do would be to “scale” the monsters up based on the player level fairly easily by adding multipliers in the function where Monsters get created. You’ll need to pass in the Player’s level to do this.

      If you wanted to have certain monsters spawn at certain levels, this could also be doable. I’d probably approach this by creating a generation table (array) for a chunk of levels -- e.g. one for levels 1-4, 5-8, 9-12, etc… The table would contain an entry for each monster that could spawn. Then your code would do this:
      * Determine which table to use
      * Roll a random number between 0 and the number of entries in the table
      * Index the table to see which monster should be created

      That way, slimes could be in the table for levels 1-4, but not 5-8 or 9-12.

      • Chris

        So should I make a few variations of this?

        Monster::MonsterData Monster::monsterData[Monster::MAX_TYPES]
        {
            { "dragon", ‘D’, 20, 4, 100 },
            { "orc", ‘o’, 4, 2, 25 },
            { "slime", ‘s’, 1, 1, 10 }
        };

        • Alex

          You could do that, but then you’d be defining some monsters more than once if they appear in multiple lists. But that’s probably the simplest approach.

          Alternatively, you could use a level of indirection by building tables of array indices, picking a random index from that table, and using that index to look up the monster.

          For example, table 1 might have two entries: 0 and 1. You’d pick one at random and then use it as the index for which monster to create (0 = dragon, 1 = orc). The upside to this approach is it reduces redundancy. The downside is that if you add new Monsters into the middle of the array, your indices change.

          • Chris

            Okay. So if I were to make multiple variations of the previously mentioned lookup table, how would I differentiate which one to use, and how would I make sure that each one is only used when the player is a certain level?

            • Chris

              Actually, would it be possible to call the Player class into the lookup table? This way, we could use the player’s level to increase mob health, damage, and gold drops. As of right now, I haven’t figured out a way to call the Player class into the lookup table, as a compile error occurs.

              • Alex

                Better to pass the player level in separately since that’s the only part of the Player class you need. It keeps things simpler since it’s just an integer.

            • Alex

              Pass in the player level as a parameter, and then use if statements to select which code path to run.

              Something like this:

              • Chris

                Passing in the Player class as such:
                Monster::MonsterData Monster::monsterData[Monster::MAX_TYPES](Player &p)
                Gives an error (array of functions is not allowed).

                Is there another way to pass it in?

                • Alex

                  Monster::monsterData is an array variable. You can’t pass a variable into another variable.

                  You should pass the Player (or an integer representing the level) into the getRandomMonster() member function and use it there.

                • Chris

                  I figured it out. Thanks a ton for helping me!

  • john

    Hi Alex, I have two questions and one comment. First, is it a good approach to add attackMonster and attackPlayer to the appropriate class?
    Second, is there any advantage of this:

    over this:

    As for the comment, could you please add to the description of the last exercise how the player receives gold? If you don’t go through the solution code or make some guess, it is unclear (I guess you forgot to add it under the bullet point that describes what happens when the monster dies).

    • Alex

      1) What respective class do you mean?
      2) Personally I think either is fine (as long as you’re not allocating dynamic memory inside the loop). Though I’d use a while(1) loop instead of a do while(1) loop to make it clearer from the outset the loop is infinite.
      3) Thanks, I updated the lesson description to mention how gold should work.

      • john

        I mean adding attackMonster to the Player class and attackPlayer to the Monster class.

        • Alex

          You could, but this means the Monster class is now dependent on Player, and the Player class is dependent upon Monster. You’ll have to find some way to resolve that circular dependency.

          • john

            I did it the way it was done in the chapter 8.13. Wouldn’t it make sense to make them member functions? For example Monster HAS an attack attackPlayer. For me not using member functions goes against the whole idea of why we have classes. We could just put all other member functions outside the class as well and just make objects the function parameters. What’s your opinion?

            • Alex

              You could argue this one several ways. If you look at it one way, since the Player is attacking a Monster, and the Monster is attacking a Player, it makes sense to have the doer have the member function, and take the receiver of that action as a parameter. However, another way to look at it is that Monster and Player are just pawns in a game, and it’s the game that allows them to attack each other. So you could say that the attack functions should go at the game level, which could be in a Game class, or as normal functions in the global scope. I’m not sure I can say one is definitely better than the other.

  • john

    "However in this case, because all of our monster attributes are predefined (not random)…" I think a better description would be "known in advance" rather than "not random". What do you think?

    • Alex

      “known in advance” wouldn’t add anything to the sentence, as it’s redundant with “predefined”. I added “(not random)” to make it clear what the alternate approach would be.

  • Jeffry

    HI Alex,

    In problem 3d) in the code section with Main().   You have an extra } on line 8 and are missing your return 0;

  • Hugh Mungus

    Hey Alex,
    Fun Quiz

    Here’s my code.

    The health and damage of the dragons may need some tweaking.

    • Jeffry

      In section 3f there is a typo.

      In the line that says "If the monster does not die, the monster attacks the player back. The player’s health is reduced by the player’s damage."

      it should be …The player’s health is reduced by the monsters damage."  (not the players).

  • Avinash K

    Hi Alex,

    I have doubt for question 1e, how the ‘d’ object of class D2 decided which print() to be used ? will the inherited functions from base class in Derived class be inherited to D2 class ? If so could you please explain me which print() does it give preference to ?

  • Soli

    Hi Alex:
    I made a general function to take care of all the attacks, and set two Creature& as parameters:

    It worked fine, but something bothered me: when passing Player& or Monster& to the function, will they be implicitly casted to Creature&? If so, will this be a good practice compared to two specific functions ie. player_attack_monster and monster_attack_player ?

    And thank you for the wonderful tutorials!

    • Alex

      Yes, Player and Monster will always be implicitly cast to a Creature. This a good thing since the logic is the same for Players and Monsters attacking each other. Generally, it’s good to write your functions using base classes instead of derived classes (if that makes sense for what you’re trying to do), as they will work for a broader range of objects and you will get more reusability out of them.

  • Hi Alex, I was messing with the code from the exercise #3 trying to create a bit more complex combat system. Basically I removed the damage member, replaced it with strength and agility and created a new method for Creatures called attack. Now, not every attack is successful, the chances of an attack succeding depend on the attackers and the defenders agilities.

    And replaced attackPlayer and attackMonster with the following fightMonster function

    Everything works fine, but what if I want to print different kind of messages on the attack function based on if the attacker is the player or a monster?. Something like if the attacker is the player print "Your attack is successful" but if the attacker is the monster "The Dragon’s attack is successful". Would it be a good practice to detect if the attacker is a player or a monster inside the method? How could it be done? Actually I’m not sure that these messages should be printed in the function at all maybe the attack function should only return a boolean to say if the attack was successful or not… but then where would I print the messages? Any ideas?

    • Alex

      Probably the easiest option would be to use a virtual function to be able to differentiate a Monster from a Player, then call that function from within the attack() function, and use the result to conditionalize your messaging.

      Alternatively, you could write a separate attack() function for players and monsters, and specialize the messaging in each one (though you would probably only want to do this if the logic for players and monsters attacking was different, otherwise you’d be duplicating logic).

  • surya

    I got myself into a problem. So I defined my classes in Player.h and Monster.h. both are included in the main.cpp file.
    Then I created the

    in Monster.h and

    in Player.h respectively. so I had to include resp header files there too.
    But now the compiler complaints that the Player in attackPlayer is not defined. I presume it’s because of the circular dependency and the inclusion in the main.cpp file. Is there a workaround to this problem?

    • Alex

      Yes, you have a circular dependency issue. There are quite a few options. Some of the easier ones:
      * Forward declare the classes in each other’s headers, and use pointer parameters rather than references.
      * Move these functions elsewhere (as non-member functions, or as member-functions of a 3rd class that can see the full definition of both classes).

      • Surya

        i tried the first one and now it shows the following error:
        error: invalid use of incomplete type ‘class Monster’

        • Alex

          This means you’re doing somewhere where the compiler needs to see the full definition of Monster, but you’ve only provided a forward declaration. Do you have any Monsters being passed by value or reference? Oh, also the code for the functions that use the Monster pointer will need to be put in the .cpp (which should include monster.h) as those will need to see the full definition for Monster, not just the forward declaration.

  • Maolin

    Hi Alex,

    small typo at line 102 of the solution for 3f:

    best, Maolin

    • Alex

      Yes, it’s redundant, though I like to leave it in just in case I reorder or add elements later. I’ve removed it in the examples for simplicity.

  • Chris

    Hi Alex,

    In 3b solution, maybe you forgot to made hasWon() function. In 3d solution,  maybe there is typo at main function, there are 2 closed brackets in there. Last one in 3f sample game session, the first encounter is slime which is have 10 gold but it said "You found 5 gold"

  • HWANG SEHYUN

    I have 3 questions!

    1. In Question 3d. When I didn’t use ‘static’ for

    , there’s an runtime error.

    I could not find the reason.

    2. In solution 3d, Is there any reason for you to not used ‘static’ for

    and

    ? These variables should belongs to the class(not object), isn’t it? (for memory efficiency)

    3. In solution 3d, you wrote

    Why did you used

    here?(not std::string)

    • Alex

      1) Static members are shared between all objects of the class, and must be initialized at program startup. That means we have to specify an initializer for the member outside of the class, so that the compiler knows what to initialize the static member with. If you remove the static keyword, then the member is no longer shared -- each object of the class has their own copy of the member, and that member can only be initialized when the object is created (as part of the constructor).

      2) Type definitions nested inside the class don’t need to be made static because the type definitions don’t instantiate the type. We’re just telling the compiler how a type is defined. Static members do get instantiated, and the static is needed to differentiate normal members from static members.

      3) const char* is often a better choice when you need a variable that holding a string literal. All of the string literal monster names get stored in memory somewhere. When monsterData is initialized, with a const char *, the compiler simply sets the pointer to point at the string literal, and we’re done. With a std::string, the std::string constructor would instead get called with the string literal as an argument. This would dynamically allocate memory and make a copy of the string. This ends up taking a lot more time and memory to do the initialization. So unless you REALLY need the functionality of std::string for some reason, const char * is the better choice in this one, specific case.

      • HWANG SEHYUN

        Thanks!

        Q) Then why did you used "string" for "Creature" class’s member variable "m_name"?
        Which functionality should I focus to distinguish the usage between "string" and "const char*"?

        • Alex

          There are two types of Creatures in this program: Monsters, and the Player. Monsters must use a name from the monsterData table, but a Player can have any name they choose.

          Because Creatures must support both usages, our Creatures use a std::string. In the Monster case, we initialize the std::string from the string literal out of the monsterData table.

          Basically, use const char* and string literals when you’re only dealing with hardcoded strings (which is the case in the monsterData table). Use std::string when you need to deal with any other kind of string (user input, or strings that may need to be modified later).

  • ali

    hello, Alex, this is my solution it runs good and gives what expected but there are differences between you and my if you have some time just look at my code and tell me if I have Intellectual errors I don’t ask about syntax or grammes of the c++ but if you noticed that I think wrongly tell me please..

  • Yatir

    the above lines made my compiler print ‘.\n’ as what i guess their ascii numbers ^^

  • Nathan

    In part 3e, the main method contains the following line of code:

    while getRandomMonster is defined as

    The compiler issued me an error here because you are trying to assign a nonconst reference to an anonymous variable which will be destroyed. It worked when I changed it to a regular variable.

    • Alex

      Fixed. Thanks for pointing that out.

      • Chen

        Hi Alex,

        Please correct me if I’m wrong.

        The ‘return’ statement invokes the default copy constructor, thus the getRandomMonster() returns an l-value.

        My compiler doesn’t complain about your initial code.

        btw, thanks for designing such a great quiz!

        • Chen

          Sorry, my bad. Seems like it’s a bug of MS VC++ compiler.

          Surprisingly the code below compiles with no problem with VS2015 (which won’t compile using CodeBlocks):

          • Alex

            I don’t know if I’d call it a “bug” of Visual Studio, but it’s certainly one area where Visual Studio is permissive in letting you do something that’s not officially part of the C++ spec.

            You should only be able to assign the result of getRandomMonster() to a normal variable or a const reference (not a non-const reference).

  • Marshall H Crenshaw

    returns an anonymous Monster instantiated in the return statement.

    seems to copy this Monster object to m.
    I just don’t see how this copy can be made with the struct and enum in the class (unless this is allowed by a shallow copy?)

    I tried to just pass a reference to the random monster created but can’t figure out how to do that.
    Would it be better to make a random monster and pass a reference back, or can’t that be done?

    • Alex

      Yes, it’s just making a shallow copy, which is fine because Monsters don’t have any pointers or dynamic memory allocations to screw up a shallow copy. The compiler will probably elide this shallow copy.

      You don’t want Monster::getRandomMonster() to pass a reference back because then you’ll be referencing a local variable that is going out of scope.

  • Lyle

    1a solution  - last line should be
    ~Base()
    The destructor is run before main() completes.

Leave a Comment

Put C++ code inside [code][/code] tags to use the syntax highlighter