Search

4.x — Chapter 4 comprehensive quiz

Quick review

We covered a lot of material in this chapter. Good job, you’re doing great!

A block of statements (aka. a compound statement) is treated by the compiler as if it were a single statement. These are placed between curly brackets ({ and }) and used pretty much everywhere.

Local variables are created at the point of variable definition, and destroyed when the block they are declared in is exited. They can only be accessed inside the block in which they are declared.

Global variables are created when the program starts, and are destroyed when it ends. They can be used anywhere in the program. Non-const global variables should generally be avoided because they are evil.

The static keyword can be used to give a global variable internal linkage, so it can only be used in the file in which it is declared. It can also be used to give a local variable static duration, which means the local variable retains its value, even after it goes out of scope.

Namespaces are an area in which all names are guaranteed to be unique. Use of namespace is a great way to avoid naming collisions. Avoid use of “using statements” outside of functions.

Implicit type conversion happens when one type is converted into another type without using a cast. Explicit type conversion happens when one type is converted to another using a cast. In some cases, this is totally safe, and in others, data may be lost. Avoid C-style casts and use static_cast instead.

std::string offers an easy way to deal with text strings. Strings are always placed between double quotes.

Enumerated types let us define our own type where all of the possible values are enumerated. These are great for categorizing things. Enum classes work like enums but offer more type safety, and should be used instead of standard enums if your compiler is C++11 capable.

Typedefs allow us to create an alias for a type’s name. Fixed width integers are implemented using typedefs. Typedefs are useful for giving simple names to complicated types.

And finally, structs offer us a way to group related variables into a single structure and access them using the member selection operator (.). Object-oriented programming builds heavily on top of these, so if you learn one thing from this chapter, make sure it’s this one.

Quiz time!

Yay.

1) In designing a game, we decide we want to have monsters, because everyone likes fighting monsters. Declare a struct that represents your monster. 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. If you’re using C++11, use an enum class for this. If you’re using an older compiler, use an enumeration for this.

Each individual monster should also have a name (use a std::string), as well as an amount of health that represents how much damage they can take before they die. Write a function named printMonster() that prints out all of the struct’s members. Instantiate an ogre and a slime, initialize them using an initializer list, and pass them to printMonster().

Your program should produce the following output:

This Ogre is named Torg and has 145 health.
This Slime is named Blurp and has 23 health.

C++11 solution: Show Solution

non-C++11 solution: Show Solution

5.1 -- Control flow introduction
Index
4.8 -- The auto keyword

57 comments to 4.x — Chapter 4 comprehensive quiz

  • Gerrod

    The non-C++11 solution is wrong. The code won’t compile because MonsterType isn’t a class or namespace. You need to get rid of class in enum class and delete all the MonsterType:: and just put the monster type.
    This compiles on CodeBlocks 13.12

  • Eric

    My code is nearly identical, but I opted to put both the Enumeration and Struct inside header files.  I think it is worth mentioning that the order in which you #include the header files in EXTREMELY important.

    Compiles fine

    does not compile and gives plenty of errors that are very misleading.  It took a me few minutes to figure out why it wasn’t working.  Thought it was worth posting in case someone else does the same thing I did.

    • Alex

      If the order of includes makes a difference, then you did something incorrectly in one of your header files. 🙂

      In this case, you have something in Structs.h that uses something in Enums.h. Therefore, Structs.h should #include Enums.h itself.

      Once you do that, it won’t matter which order you #include them in your main program.

  • zhiwei

    This is my solution. Seems your’s is better. I need to review my code.

  • Todd

    Typos.

    "A block of statements (aka. a compound statement) that is treated by the compiler as if it were a single statement." (remove ‘that’)

    "Typedefs allow us to alias (give an alias for) a type’s name." (alias is a noun, not a verb)

    "And finally, structs offer us a way to group related variables into a single structure, (remove comma) and access them…"

    For the following, I believe the function names should start with a lowercase letter:

    "Write a function named PrintMonster()…"

  • Minh

    There is a mistake in non-C++11 solution.
    At line 43, in GetMonsterTypeString, the letter "G" must not be capitalized. It must be getMonsterTypeString.

  • Cameron

    Can i ask you something?
    When you start this program do you start with main?
    I get kind of lost when i just start ilon my own
    Hopefully it will come with time

    • Alex

      Yes, programs always start at the top of function main().

    • Keith

      I may be wrong, but the way I read Cameron’s question was ‘how do you approach writing a solution for this?’.

      Yes, this will definitely come with time and experience. The way I approached this was to look at what data we needed to store and manipulate, and start with this - I defined the Monster struct first, and included a variable of MonsterType which I knew would be an enum. Then I wrote the enum.

      Once I knew what my struct looked like the next thing I did was to write the function to print it out. Then finally I wrote main() to create some Monster variables and send them to the print function.

      No real right or wrong way, but sometimes it can help to focus on how you are going to store the data (a struct in this case), get that right then work on the functions to do something with that. Main() is really the glue that holds all the pieces together, and often isn’t doing much more than creating variable instances and firing them off to functions - that’s why I did that bit last once I was certain how the structures and the function were going to operate.

      • Alex

        Aah, you could be right. My method for tackling this would be similar to yours. I’d start with the data (enums, structs, classes, etc…). I’d probably sketch out main() next. Then I’d progressively write the functions that were called in main().

  • Janee

    Hi there,

    How do you check the compiler version? I am using Microsoft visual studio 2013. Anyway to find out? thanks in advance

  • R00kie

    Hi Alex,

    I was trying to do this program with two structs instead of one struct and one enumeration. Is that possible ? I didn’t quite figure it out to make it work with 2 structs and not use the enumeration.

    One more thing ..where can I find more quizes about structs ? it seems like an important chapter and I wanna do more exercises with structs..where can I find some ?

    It would be great if you could give us more exercises at the end of chapter 4.

    • Alex

      Possible? I suppose. Sensical? Unsure. You’d have to tell me more about how you were trying to solve the problem with 2 structs.

      For now, I’d advise looking on Google for more information on structs. Structs are super-important because their cousin, the class, is a relative of the struct. However, we’ll cover classes in extensive detail later, so if you understood the struct lesson reasonably well, you shouldn’t have anything to worry about.

  • Rob G.

    This is about the string. A more efficient program would be to use structs alone, and use strings instead of enum members. On the other hand Alex maybe you wanted us to struggle with the enums…
    Am really getting a lot out of the course. Thx for ll the hard work u put into this!!

    Load up your objects and manipulate them

  • Chris

    I know I wrong at the member Monster(I didn’t insert the monster type). But I am so curious why the result of my code like this(codeblocks) :
    ___________________________________________________________
    This 1 is named Torg and has 145 health.
    This 1 is named Blurp and has 23 health.

    Process returned 0 (0x0)   execution time : 0.009 s
    Press any key to continue.
    ___________________________________________________________

    1?? here’s the code. I have checked them all and didn’t found the mistake.

    Thank you.

    • Alex

      This function is wrong:

      Your second line calls function type() but discards the result, and inside your cout statement, you’re printing the address of function type() rather than making a call to it.

      This should work:

  • Indorfin

    I just want to say thank you so much for these tutorials! This is one of the few sites I disable ad-block on.

    I also wanted to say that I like what you chose for a quiz. The problem is, I made something similar as soon as enumerated classes were explained!

    Once I learn how to make it keep repeating turns until someone dies, I’ll be able to make it possible for someone to "win"! Less than a week ago I didn’t know ANY coding, so even this little faux-game is pretty amazing to me! Thank you for showing me how easy it is to get started.

    • Alex

      You’re welcome! Loops (which will allow you to make repeating turns) are explained in the very next chapter. You’re almost there!

    • I have improved the code a little…

  • Radion

    Unfortunately, the code for C++11 version doesn’t work. But when I remove class from enum and perform all related changes, everything works perfectly.

  • J3ANP3T3R

    Just a quick question when should you always use ";" ? on all of our single line statements we used ; to terminate a line of code but i noticed on some of our functions like main() and any other function we create in the main.cpp file we dont use ; but on some of our function we use ; and whether i use ; or not on main(){}; the compiler wont make any error messages. what should be the standard practice ?

    • Alex

      You should use ; whenever you’re terminating a statement, whether it’s one line or many.

      > but on some of our function we use ;

      We only use ; for forward declarations. Function definitions don’t need a ; after the closing block -- I’m not sure why.

  • J3ANP3T3R

    it happened again. im having errors with the instantiation.

    // RPG.h
    #ifndef RPG_H
    #define RPG_H

        enum class monType
        {
            MT_OGRE,
            MT_DRAGON,
            MT_ORC,
            MT_GIANTSPIDER,
            MT_SLIME
        };

        struct MonsterInfo
        {
            std::string name = "UNKNOWN";
            int health = 100;
            monType type = monType::MT_OGRE;
        };

    #endif

    // RPG.cpp
    #include "stdafx.h"
    #include <iostream>
    #include "RPG.h"

    void printMonster(MonsterInfo monsterToPrint)
    {

    }

    int main()
    {

        MonsterInfo monOgre1 { "OGRE 1",100,monType::MT_OGRE };
        return 0;
    }

    the line MonsterInfo monOgre1 { "OGRE 1",100,monType::MT_OGRE }; is giving me this error message
    1>d:visual studio community 2015peters projectsrpgrpgrpg.cpp(16): error C2440: ‘initializing’: cannot convert from ‘initializer list’ to ‘MonsterInfo’

    EDIT : oh never mind it seems that removing the default values for the struct removes this error. but how can i add default values for members of the struct that was not initialized ?

    • Alex

      Visual Studio 2015 won’t let you use both non-static member initialization and initializer lists with a struct. I hope they fix that in the next major version.

      In the meantime, you’ll either have to choose one or the other (I’d recommend the initializer lists), or upgrade to full classes.

  • Nyap

    • Nyap

      why can I use fixed-width integers without the cstdint header? is cstdint #included in iostream?

      • Nyap

        hmm it seems like it is
        also, I did the quiz wrong, because it always says "ogre" regardless of the monsters type
        I fixed it (if anybody cares lol): http://pastebin.com/qtXivgyq

      • Alex

        No idea, it’s possibly included from some other file you’ve included, or your compiler is including it by default.

        But don’t rely on that -- you should always explicitly include whatever headers you need.

  • Fern

    • Alex

      There are a couple of approaches you could use. If the number of creature types is limited (e.g. 64 or less), you could turn CreateType into a fixed-width integer and set up the CreateType enum as a set of bit flags. Then you could do something like:

      Another way to do this would be to declare creatureType as an array of CreatureType instead of a single CreatureType. Then you could do:

      We cover arrays in chapter 6. But for this use, bit flags seems like the better choice.

  • subh

    I wrote the code of question-1 in another way without using getMonsterTypeString(monster) function as below, which produces intended result.
    Does my code have any limitations?
    #include <iostream>
    #include <string>

    // Define our different monster types as an enum
    using std::cout;
    using std::endl;
    enum class MonsterType
    {
        OGRE,
        DRAGON,
        ORC,
        SPIDER,
        SLIME
    };

    struct Monster
    {
        MonsterType type;
        std::string name;
        int health;
    };

    Monster printMonster(Monster monster)
    {
       cout << "This Monster name is: " << monster.name ;
       cout << " and has " << monster.health << " health\n";
       return monster;
    }
    int main()
    {
        Monster ogre = { MonsterType::OGRE, "Torg", 145 };
        Monster slime = { MonsterType::SLIME, "Blurp", 23 };

        printMonster(ogre);
        printMonster(slime);

        return 0;
    }

    • Alex

      Seems fine, though I don’t know why function printMonster() is returning anything. It would be better if the function returned void and didn’t have a return statement.

  • KIRPAL

    Can you help me to understand this line,

        MonsterType type;

    why did you put MonsterType before a variable “type”?

    i’m a beginner

    • Alex

      ‘type’ is the name of the variable being declared, and MonsterType is the type of the variable being declared. This is similar to how we declare variable x as being an integer via “int x;”. In this case though, instead of an integer, we’re using an enumerated type (MonsterType) of our own creation.

  • Abhi

    Any more programs to practice on?
    I failed quiz 1 so I want to do another to ensure I am trained.

    Maybe you can add more quizes in each chapters comprehensive quizzes section

  • Damien

    Hi Alex,

    When trying the quiz i ended up trying to do it a different way to your solution. But had problems.

    I got an error (line 23): "no operator "<<" matches these operands".
    and a linker error: "binary ‘<<‘ no operator found which takes a right-hand operand of type ‘MonsterType’ (or there is no acceptable conversion)"

    I understand your solution fine. But am just wondering why this error pops up in mine. Is it something to do with the type for monster not being a string???

    Thanks!

    • Alex

      enum classes are considered a unique type, and std::cout doesn’t know how to output these. You’ll need to write a function to convert a MonsterType to a const char* or std::string (preferred).

  • kris

    I think their should be more clarity you cause names to run into eachother alot although sometimes lowercase it confuses the reader. but i understand each block the previous variable doesn’t matter but you know

  • Gerald

    Maybe your question should say that you want a string function? Did it exactly like your answer except I didn’t do the std::string function. No biggy, just a thought.

    Otherwise keep up the good work. Your website is awesome!

  • Arturo Ortiz

    #include <iostream>

    using namespace std;

    struct MonsterStruc{

        string name;
        int health;

    };
    enum MonstersEnum{

        MONSTER_OGRE,
        MONSTER_DRAGON,
        MONSTER_ORC,
        MONSTER_GIANT_SPIDER,
        MONSTER_SLIME
    };

    string getMonster(MonstersEnum monsterE){

        if (monsterE == MONSTER_OGRE)
            return string("Ogre");
        if (monsterE == MONSTER_SLIME)
            return string("Slime");

    }
    void printMonster (MonsterStruc monster, MonstersEnum monster1 ){

        cout<< "This is an " << getMonster(monster1) << " his name is " << monster.name << " and his health is " << monster.health << endl;

    }

    int main()
    {
        //STRUCT
        MonsterStruc monster1 = {"Durotan", 140};
        MonsterStruc monster2 = {"Frinu", 200};
        //ENUM
        MonstersEnum monsterE1(MONSTER_OGRE);
        MonstersEnum monsterE2(MONSTER_SLIME);
        //FUNCTIONS
        printMonster(monster1, monsterE1);
        printMonster(monster2, monsterE2);

           return 0;
    }

    Hey Alex I would like to know if theres any advice you can give me about my code. It runs and does the job but i dont know if i could improve it. (Nice tutorials btw) Thanks!.

    • Alex

      A variable of type MonsterEnum should be inside MonsterStruc rather than declared in main(). Then printMonster() only needs one parameter.

      Also, don’t use “using namespace std;”

  • reznov

    Written it to be somewhat functioning as a game. So you tell the game your character level (where you left off last time I guess). Then it will match a monster to your level and you can choose to fight or run. If you fight you will win because there’s no RNG functions in my toolkit yet and you will gain a level of experience. This will prompt a next fight request after every fight until eventually updating the monster type when you pass the required level or if you choose to run.

    This program raised a new question about structs however.. How can I build this program in a way I only have to only pass 1 variable to my function and still be able to read out the monster stats like slime.type ? I tried nesting structs but it didn’t do much good :\

    #include <iostream>

    struct MonsterStats
    {
        std::string type;
        std::string name;
        int health;
        int level;
    };

    void printMonster(MonsterStats slime, MonsterStats spider, MonsterStats orc, MonsterStats ogre, MonsterStats dragon, int charLevel)
    {

        if(charLevel>=slime.level&&charLevel<spider.level)
            std::cout << "This " << slime.type << " is named " << slime.name << " and has " << slime.health << " health.\n";
        else if(charLevel>=spider.level&&charLevel<orc.level)
            std::cout << "This " << spider.type << " is named " << spider.name << " and has " << spider.health << " health.\n";
        else if(charLevel>=orc.level&&charLevel<ogre.level)
            std::cout << "This " << orc.type << " is named " << orc.name << " and has " << orc.health << " health.\n";
        else if(charLevel>=ogre.level&&charLevel<dragon.level)
            std::cout << "This " << ogre.type << " is named " << ogre.name << " and has " << ogre.health << " health.\n";
        else if(charLevel>=dragon.level)
            std::cout << "This " << dragon.type << " is named " << dragon.name << " and has " << dragon.health << " health.\n";
    }

    int main()
    {
        using namespace std;

        MonsterStats ogre{"Ogre","Grumble",345, 16};            //Type, Name, Health, Level
        MonsterStats dragon{"Dragon","Spyro", 890, 21};         //Type, Name, Health, Level
        MonsterStats orc{"Orc","Rakanishu", 165, 11};           //Type, Name, Health, Level
        MonsterStats spider{"Giant Spider", "Arachnid", 80, 6}; //Type, Name, Health, Level
        MonsterStats slime{"Slime", "Blurp", 35, 1};            //Type, Name, Health, Level

        std::string getChoice="y";
        int charLevel;
        cout << "Please enter the level of your character to proceed to battle: ";
        cin >> charLevel;
        if(charLevel>0)
        {
            while(getChoice=="y")
            {
                printMonster(slime, spider, orc, ogre, dragon, charLevel);

                cout << "Do you want to fight the monster? (y/n)";
                cin >> getChoice;
                if(getChoice=="y")
                {
                    cout << "You destroyed the monster and gained an experience level!\n\n";
                    charLevel+=1;
                }
                else
                    cout << "You ran like Forest Gump and got out of the fight safely!\n\n";
            }
        }
        else
            cout << "Please create a character in order to play the game.";
        return 0;
    }

    • Alex

      Your program is organized a little strangely. I think you should do the following:
      1) Create a new function named generateMonster
      2) generateMonster should only take charLevel as a parameter, and return a monsterStats.
      3) Move the MonsterStats variables at the top of main() into generateMonster().
      4) generateMonster can do the same logic to determine what monster to return to main().
      5) modify printMonster to take a MonsterStats parameter and print out the stats for just that one monster passed in
      6) main() can call generateMonster() to return a monster of the appropriate type based on level, and printMonster() to print the monster’s stats.

      This will be much more modular than what you have.

      • reznov

        Great! Thanks 🙂
        I’ve always been informed through our beloved programmers on stack overflow that later in this tutorial I will learn to do MonsterStats by array that can be editted with a loop and replace the infinite if else logic with some kind of a std::sort operator and some pointers. So definately something to look into for me in the later chapters to maybe actually turn this into some proper coded minigame 🙂

        So far the code looks like this after your advice, still working and lengty in code but finally rid of the long function parameter:

        #include <iostream>

        struct MonsterStats
        {
            std::string type;
            std::string name;
            int health;
            int level;
        };

        MonsterStats generateMonster(int charLevel)
        {
            MonsterStats ogre{"Ogre","Grumble",345, 16};            //Type, Name, Health, Level
            MonsterStats dragon{"Dragon","Spyro", 890, 21};         //Type, Name, Health, Level
            MonsterStats orc{"Orc","Rakanishu", 165, 11};           //Type, Name, Health, Level
            MonsterStats spider{"Giant Spider", "Arachnid", 80, 6}; //Type, Name, Health, Level
            MonsterStats slime{"Slime", "Blurp", 35, 1};            //Type, Name, Health, Level
            MonsterStats error{"","",0,0};

            if(charLevel>=slime.level&&charLevel<spider.level)
                return slime;
            else if(charLevel>=spider.level&&charLevel<orc.level)
                return spider;
            else if(charLevel>=orc.level&&charLevel<ogre.level)
                return orc;
            else if(charLevel>=ogre.level&&charLevel<dragon.level)
                return ogre;
            else if(charLevel>=dragon.level)
                return dragon;
            else
                std::cout << "Please create a character in order to play the game.";
                return error;
        }

        void printMonster(MonsterStats generated)
        {

            std::cout << "This " << generated.type << " is named " << generated.name << " and has " <<
            generated.health << " health.\n";

        }

        int main()
        {
            using namespace std;

            std::string getChoice="y";
            int charLevel;
            cout << "Please enter the level of your character to proceed to battle: ";
            cin >> charLevel;
            if(charLevel>0)
            {
                while(getChoice=="y")
                {
                    MonsterStats generated = generateMonster(charLevel);
                    printMonster(generated);
                    cout << "Do you want to fight the monster? (y/n)";
                    cin >> getChoice;
                    if(getChoice=="y")
                    {
                        cout << "You destroyed the monster and gained an experience level!\n\n";
                        charLevel+=1;
                    }
                    else
                        cout << "You ran like Forest Gump and got out of the fight safely!\n\n";
                }
            }
            else
                cout << "Please create a character in order to play the game.";
            return 0;
        }

        As said in later chapters I will work on how lengthy it is with the advice I got from the guys and girls over at stackoverflow, they tend to go slightly out of scope of what I’ve learned.

        • reznov

          Oh whoops I did the error handling for charLevel<1 twice. Just the one in main should do since I don’t expect the charLevel-incrementing while function to decrease it below 1 ever.

        • Alex

          Yup, next step is to move your monsters into an array and automatically iterate over the array. 🙂 That will make your program more extensible, as you can add new monster by updating the array (no other code changes necessary).

  • Matt

    The third paragraph needs an update(local variables created at definition).

  • Matt

    In the sentence you updated(3rd paragraph), you forgot to rewrite the second half of the sentence (it still references the old phrasing).

  • James

    Hi Alex,

    Thanks for a great tutorial. I really appreciate the rigour in your approach.

    Regarding the quiz question; I wanted to add a unique ID to each of my monsters and, recalling your discussion of static duration variables in section 4.3, used a function similar to generateID (monsterID in the below). When I instantiate (correct usage?) a monster, I use non-static member initialisation to assign the value returned by monsterID to Monster::id, a const int.

    I have three questions at this point:
    Is it proper to use non-static member initialisation for some members, and not others?
    Is it proper to use a non-static member initialisation that calls a function?
    For structs, you advise using uniform initialisation instead of non-static member initialisation in C++11. You note that mixing is possible in C++14, but don’t discuss the ethics of this practice - what is your stance?

    Best,
    James

    My code:

    • Alex

      Great questions!

      > Is it proper to use non-static member initialisation for some members, and not others?

      Yes. Your use of non-static member initialization here is totally appropriate since the initialization value of the member doesn’t vary per constructor.

      > Is it proper to use a non-static member initialisation that calls a function?

      Yes, but with one caveat. You should only call globally scoped functions (like monsterID() is) or static member functions (which we’ll cover once we discuss classes). Calling normal member functions is asking for trouble. But you’re fine here.

      > For structs, you advise using uniform initialisation instead of non-static member initialisation in C++11. You note that mixing is possible in C++14, but don’t discuss the ethics of this practice - what is your stance?

      Mixing is not only fine, it’s a good idea, as it will ensure your struct members are always initialized while also allowing you to assign your own initialization values as appropriate. I’ve made a note of such in the lesson.

Leave a Comment

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