Search

6.x — Chapter 6 summary and quiz

Quick review

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

A compound statement or block is a group of zero or more statements that is treated by the compiler as if it were a single statement. Blocks begin with a { symbol and end with a } symbol, with the statements to be executed placed in between. Blocks can be used anywhere a single statement is allowed. No semicolon is needed at the end of a block. Blocks are often used in conjunction with if statements to execute multiple statements.

User-defined namespaces are namespaces that are defined by you for your own declarations. Namespaces provided by C++ (such as the global namespace) or by libraries (such as namespace std) are not considered user-defined namespaces.

You can access a declaration in a namespace via the scope resolution operator (::). The scope resolution operator tells the compiler that the identifier specified by the right-hand operand should be looked for in the scope of the left-hand operand. If no left-hand operand is provided, the global namespace is assumed.

Local variables are variables defined within a function (including function parameters). Local variables have block scope, meaning they are in-scope from their point of definition to the end of the block they are defined within. Local variables have automatic storage duration, meaning they are created at the point of definition and destroyed at the end of the block they are defined in.

A name declared in a nested block can shadow or name hide an identically named variable in an outer block. This should be avoided.

Global variables are variables defined outside of a function. Global variables have file scope, which means they are visible from the point of declaration until the end of the file in which they are declared. Global variables have static duration, which means they are created when the program starts, and destroyed when it ends. Avoid dynamic initialization of static variables whenever possible.

An identifier’s linkage determines whether other declarations of that name refer to the same object or not. Local variables have no linkage. Identifiers with internal linkage can be seen and used within a single file, but it is not accessible from other files. Identifiers with external linkage can be seen and used both from the file in which it is defined, and from other code files (via a forward declaration).

Avoid non-const global variables whenever possible. Const globals are generally seen as acceptable. Use inline variables for global constants if your compiler is C++17 capable.

Local variables can be given static duration via the static keyword.

Using statements (including using declarations and using directives) can be used to avoid having to qualify identifiers with an explicit namespace. These should generally be avoided.

Typedefs and Type aliases allow the programmer to create an alias for a data type. These aliases work identically to the aliased type. They can add legibility and reduce maintenance to code.

The auto keyword has a number of uses. First, auto can be used to do type inference / type deduction, which will infer a variable’s type from its initializer. Auto can also be used as a function return type to have the compiler infer the function’s return type from the function’s return statements, though this should be avoided for normal functions. Auto is used as part of the trailing return syntax. And finally, as of C++20, auto provides a shortcut for creating function templates. For now, you’ll generally use it for type deduction purposes.

Implicit type conversion is performed whenever one data type is expected, but a different data type is supplied. If the compiler can figure out how to do the conversion between the two types, it will. If it doesn’t know how, then it will fail with a compile error. Numeric promotion (or widening) occurs when a value from one fundamental data type is converted to a value of a larger fundamental data type from the same family (e.g. a short to an int). Numeric conversion occurs when we convert a value from a larger to a smaller type (e.g. int to short), or between different type families (int to double). Conversions that could cause loss of data are called narrowing conversions.

Explicit type conversion is performed when the programmer explicitly requests conversion via a cast. A cast represents a request by the programmer to do an explicit type conversion. C++ supports 5 types of casts: C-style casts, static casts, const casts, dynamic casts, and reinterpret casts. Generally you should avoid C-style casts, const casts, and reinterpret casts. static_cast is used to convert a value from one type to a value of another type, and is by far the most used-cast in C++.

Finally, C++ supports unnamed namespaces, which implicitly treat all contents of the namespace as if it had internal linkage. C++ also supports inline namespaces, which provide some primitive versioning capabilities for namespaces.

Quiz time

Question #1

Fix the following program:

Show Solution

Question #2

Write a file named constants.h that makes the following program run. If your compiler is C++17 capable, use inline constexpr variables. Otherwise, use normal constexpr variables. You can pick any value you like for max_class_size.

main.cpp:

Show Solution

Question #3

Complete the following program by writing the passOrFail() function, which should return true for the first 3 calls, and false thereafter.

Show Hint

The program should produce the following output:

User #1: Pass
User #2: Pass
User #3: Pass
User #4: Fail
User #5: Fail

Show Solution


S.4.4b -- An introduction to std::string
Index
6.17 -- Unnamed and inline namespaces

295 comments to 6.x — Chapter 6 summary and quiz

  • Kio

    @Alex and @nascardriver

    This is also a neat solution. It will work only if we leave the default values for the enumeration "monsterType". Because the array and enumeration start from index position "0".

    • Hi Kio!

      That's a nice solution, but arrays haven't been covered yet so it can't be used in this quiz's solution. I'd go with @std::map to have all iterators and their names side by side.

      Suggestsions
      * You're using the same name style for types, functions and variables. This will get confusing.
      * @arrayMonster should be constexpr static inside @printMonster
      (* You should add a static_assert to make sure @arrayMonster and @monstersType have the same size)

  • After 3 attempts where the output was junk (I forgot the qualifier after whichMonster() in the printMonster() routine) this version works as it should:

    However, I'm still not sure if the multiple <if...> <else if...> statements in the whichMonster() routine are the tidiest means of doing this?

    • Hi Nigel!

      Line 52, 53: Initialize your variables with uniform initialization.

      > enum class
      Good choice.

      > safety just in case there is nothing else to return
      Correct.

      > looks lumpy in the present state
      Looks alright to me.

      > I'm still not sure if the multiple <if...> <else if...> statements in the whichMonster() routine are the tidiest means of doing this?
      They're not, but they're the best way of doing it that you know of so far.

  • R310

    Help NascarDriver!

    How to I get my monsters to show their type?

    • Hi R310!

      First off:
      * Don't use "using namespace"
      * Use enum class
      * Use uniform initialization

      There is no way to get enumerator names in C++, you need to translate them manually.

      • R310

        Thank you!
        Why can't I use using namespace std?

        I'm sorry, I did not get the difference between enum and enum class?

        I am not allowed to use break in my class :(. How can I go around that?

        • > Why can't I use using namespace std?
          It can cause name collisions.

          > I did not get the difference between enum and enum class
          enum declares the enumerators unscoped, just like "using namespace", this can cause name collisions.

          > I am not allowed to use break in my class
          How come? Source code and error message please.

          References
          * Lesson 1.8a - Naming conflicts and the std namespace
          * Lesson 4.3c - Using statements
          * Lesson 4.5a - Enum classes

          • R310

            Thank you for the reply :)
            I am allowed technically but we are asked to write our code without using break, because it's ''programming style'' they say.

          • R310

            Look, now I just need to get rid of the break :) (unbrrrrrreeeeaaaak my heeeeaaarrt)

  • ayush

    is this a correct way to get output

    • nascardriver

      Hi ayush!

      * Don't use "using namespace"
      * Use enum class
      * Initialize your variables with uniform initialization
      * You're using the same naming convention for types, variables and functions, this will lead to confusion.
      * Line 26-35: Don't repeat yourself. Write a function that takes a monster as a parameter and prints it. Also, missing linebreaks.
      * @main: Missing return-statement.

  • mike

    Hi.

    According to this manual, enum types print by default as an int, so I changed the printMonster function and I replaced getMonsterTypeString(monster) with monster.type so I could get an int in the output, but instead I got an error.

    Do you know what is the error?
    Thanks.

    • nascardriver

      Hi Mike!

      What's your new code, what's the error you're getting?

      • Mike

        Hello. I'm sorry for answering so late.

        This is the code:

        • nascardriver

          enum classes cannot be implicitly converted to an int, you need an explicit cast.

  • Jack

    Hi, wanted to make it so that the user is prompted for type/name/hp before outputting the final string. Have coding experience in Java and Python, but this is my first look at C++. This code runs fine, but not sure that it's the most efficient, can't figure out how to do it better. Any thoughts?

    • nascardriver

      Hi Jack!

      Everything efficiency related hasn't been covered yet, your code is fine in that aspect (almost, see below).

      Suggestions:
      * Line 38: Use uniform initialization and ++prefix instead of postfix++.
      * Line 51: Initialize variables to 0 if you don't know their value yet.
      * @getChoice: This decreases efficiency. Don't call @std::cin.clear() and @std::cin.ignore() unless you have to. Also, duplicate code.

      * @getHealth: Duplicate code, use a do-while-loop as in @getChoice.
      * "getChoice" is a poor name, what is chosen? Return type should be @MonsterType.

  • ArmaIG

    Hi Teachers, this is the code of my Quiz, any feedback?, also a question, enums and structs cannot be forward declarated, can they?

    • nascardriver

      Hi ArmaIG!

      > enums and structs cannot be forward declarated, can they?
      They can, but you can only use pointers and references to them, because regular use requires the compiler to know the size of the struct/enum, which it can't from just a forward declaration.

      Suggestions:
      * The forward declarations of @stringMonsterType and @printMonster will cause you nothing but extra work. Move the functions above @main.
      * Use an if-else-if in @stringMonsterType,

      Everything else looks good for what has been covered so far.

      References
      * Chapter 6 - Arrays, Strings, Pointers, and References

      • ArmaIG

        Thank you nascardriver!

        I started doing forward declaration of my functions because I like to have my main on the top if possible, I don't know if that affects my code badly other than cause me more work (also serves as an index for me xD).

        What's the difference between if-else-if and full ifs?

        Regards

        • nascardriver

          When you have a return or break there's no difference. But imagine you had code like this

          Obviously @monster.type can only have one value at a time. Once a match was found you'd still be checking all other possibilities even though there's no way the condition could be fulfilled. "if" only work with return/break, if-else-if works always, so use if-else-if (or switch).

      • Simpu

        NascarDriver,So how can we use structs or enum with multiple files?(I mean the definitions in other file and uses in another for project organization purposes)
        After some Google search,I found that structs and enum should be defined in header files otherwise we will get "Incomplete type" error.
        Why can't we just forward declare a struct or enum?

        • nascardriver

          Hi Simpu!

          You can forward declare structs, classes and enums, but you'll only be able to use pointers and references to those types and you can't access their members.
          In order to have full functionality your compiler needs to know the type's size and members, which it can't from a forward declaration.
          The code on learncpp rarely uses multiple files for simplicity's sake, in real projects there's usually one struct/class/enum per file, sometimes more when they're strongly related.

  • _RryanT

    This is how I've made the Quiz:

    Can anyone point out any incoherence? I don't feel my code is good

    • nascardriver

      Hi Ryan!

      * @getMonsterType doesn't return anything if the type of @monster is not handled by the if-else-if.
      * Line 45: Use "using type_t = MonsterRace;" or don't do this at all.
      * Line 16: Add an enumerator "invalid" or similar to @MonsterRace which you use for cases like this.
      * @printMonster should call @getMonsterType and not receive the type as an argument.

  • Jon

    Just got this test working! I think I might be breaking off my code into too many unnecessary files, as I'm not sure the best practices for how to separate code between files...

    I also saw someone else in the comments having user input dictate which monster pops out, which is a cool idea! I might upgrade this code to do that next!

    Learn CPP 4.x.cpp

    idontknowwhatishouldnamethis.h

    functions.h

    functions.cpp

    • nascardriver

      Hi Jon!

      > idontknowwhatishouldnamethis.h
      monster.h

      You can use single-file projects until classes have been covered.

      Suggestions:
      * Stick to letters, numbers and underscores for file names.
      * Use uniform initialization.

      • Jon

        Okay, I spent the time afterward doing what I said I would try in the initial post and making it respond to user input. Tried posting it, but I guess the comment didn't take. Boy, did reaction to user input really increase the difficulty of this test!

        I'll stick to single files for future lessons, until advised otherwise - thanks nascardriver!

        main.cpp

        monster.h

        functions.h

        functions.cpp

        • Jon

          Also, I'm surprised that in getMonsterTypesAndIndicesAsString(), I can't use multiple += operators on strings in the same line. and write it like this:

          • nascardriver

            Use operator+ to chain strings, use operator+= to append to an existing string.

            • Jon

              Ah, that does work:

  • Daniel

    Hey Alex,

    Coming from Python, I'm very used to class initialization (`foo = Foo(bar="baz")`), and the C++ struct initialization looks a bit odd to me. So I tried this:

    ... which seemed to work just fine. Is there any functional difference between the two, and which one is idiomatic C++?

    • nascardriver

      Hi Daniel!

      There's no functional difference, but I don't see why you would want to repeat the type name.
      You should use uniform initialization for better performance and type safety

      References
      Lesson 2.1 - Fundamental variable definition, initialization, and assignment

  • J Gahr

    Hello, this is my program (for C++11). I used static_cast to help print the monster's type, and was wondering if I am making it too complicated?

  • Tony

    Yo Alex, I've done the "non C11" solution differently.

    Can this be considered okay, or is it totally wrong? It seems to work.

    • nascardriver

      Hi Tony!

      I don't understand what you're trying to achieve by using separate variables for the type. The monster's type is already stored in the monster itself.

      Line 1 (Comment): That's not an enum class, it's a normal enum, and the entries aren't called functions but enumerators.
      Line 21, 23, 25, 27, 29: Repetition of "Monster Type:". Repetitions should be avoided. The best solution is writing a function that returns the monster's type as a string like Alex did it.

  • Chetan

    • nascardriver

      Hi Chetan!

      I've added comments to your code, feel free to ask any questions that may come up.

  • Dear nascardriver!

    is it ok if I skip this due to the lack of my understanding in enums?

    • nascardriver

      Hi Ali!

      You can skip it but you'll come across enums sooner or later.
      enums are just a bunch of named integers.

    • WiseFool

      Ali, I'm usually quick to understand things, but I struggle a bit with enumerations, too.  Your problem may be like mine: I keep looking for some "there" there that just isn't there - some great purpose and use for them.  Although they're a "cool" concept, there's so little practical use for them that you have to almost contrive ways to use them.  And you end up having to use a series of if statements or a switch statement to make use of them anyway, so why bother using them?  I've concluded that the best thing about them is that they're a good way to document in english on the spot what a coded number means.
      for example:
          if (monsterType == 0)
      bad: what does that number code 0 mean?
          if (monster.type == MonsterType::OGRE)
      much better!

      • Alex

        Personally, I use enumerations all of the time. More than many of the other concepts in C++. Why? Because they allow us to define sets of things and give them descriptive names. As you note above, MonsterType::OGRE is much more descriptive than the literal 0. Also, enums pair really well with arrays (something we discuss in chapter 6), so you can use an enumerator as an array index. For example, inventory[ItemTypes::TORCH] is much more meaningful than inventory[0].

        Pretty much anywhere you are thinking about using an integer to identify or select from a small set of well-defined objects, an enumerated value is a better choice.

  • Matt

    Here's my take on this chapter's quiz.  I found that using an enum class seemed to generate more work than it saved (though that is probably because of how I used it).  I recall the lesson on enums mentioning that they don't play nicely with std::cin but I gave it a go anyway.  What I can say is that this quiz gave me a new appreciation for all of those games I played growing up.  I can't imagine what a fully-functional game's code must look like!

    • nascardriver

      Hi Matt!

      Nice code once again, here are my two cents:

      General: Inconsistent use of '\n' and std::endl
      @spawnMonster: Don't hardcode enum ids and don't let the user input an id directly, this will cause problems when modifying the @MonsterType enumeration or when the user enters an invalid id. Using a separate function to convert string <> int <> MonsterType is better (Use a switch statement like you did in @printMonster).
      Line 28: Use std::numeric_limits<std::streamsize>::max() instead of 32767 for real applications (Doesn't matter here)
      Line 32: That's correct
      @printMonster: Imagine you want to return a string or write to a file rather than writing directly to std::cout, that's going to be a lot of work updating this function. Constructing a string containing the full sentence and writing that string to std::cout is more portable.

    • Hi  Matt!
      I pretty much understood everything from your code but what did you do in line 28?

      oh and are you just a starter? because it doesn't seem so :)

      • nascardriver

        std::cin.ignore ignores all characters in the input stream until the given character ('\n') is reached (inclusive).
        You don't need to understand this now, it's part of lesson 5.10.

      • Matt

        Ali Dahud,

        Thanks, I'm flattered but I am indeed a novice.  In all honesty though, I started going through this series a couple years ago and made it to lesson 3 or so before I got deployed and forgot everything.  That said, this is my second voyage through the material up to this point.  Even so, I still have to regularly refer back to previous lessons to remember how to do something, which I think is OK, not many people remember 100% of what they learn on the first exposure.  Alex and nascardriver still find things that can be improved with my code so I'm far from the example to emulate - just a fellow student like you!

        I think your questions have already been answered.  Best of luck!

        Regards,
        Matt

  • Silviu

    Hello,
    In lesson 4.4b - An introduction in std::string , i would appreciate if was an example like in this quiz using a function of std::string.But great lessons to learn ! Thank you.

  • Andreas

    So basically if i understood this,its not recommended doing this :

    #include <iostream>

    using namespace std; // This is not recommended,right?

    int main() etc.

    I dont like using 'using namespace std;' myself, i prefer writing full std::cout/cin/string.
    I know its useful if std starts to repeat but only INSIDE functions,not at pure beginning of program,rigt?

    • nascardriver

      Hi Andreas!

      > its not recommended doing this
      Yep

      > I know its useful if std starts to repeat but only INSIDE functions,not at pure beginning of program,rigt?
      You mean having a using directive inside a function instead of an entire file?
      It's not as bad, but it's still bad.

  • Sirius

    Hi, I have a quick question about namespaces and structs. I placed the enum code for the quiz inside the struct block so I could experiment:

    Since all

    versions have to be replaced with

    versions I wanted to implement a using directive or using declaration instead of replacing every MonsterType with Monster::MonsterType however I wasn't able to get it to work properly. I tried

    and

    How would one go about getting namespaces to work in this situation, if it's at all possible? Thanks!

    • nascardriver

      Hi Sirius!

      Those assume that @Monster is a namespace. @Monster isn't a namespace so it doesn't work.

      What you are looking for is a type alias

      See lesson 4.6 (Typedefs and type aliases)

      • Sirius

        Thank you for the solution! Would you warn against placing enums inside structs in this manner?

        I quite like the aesthetic of grouping the variables together this way (and it does save some space) but I understand that it could be difficult to debug or understand if you're viewing the program for the first time, especially with type aliases all over the place.

        • nascardriver

          "Would you warn against placing enums inside structs in this manner?"
          I rarely use structs, classes do everything a struct does and more, so there's rarely a reason to use a struct over a class.
          You can place enums inside structs no problem but if you're planning on type aliasing the enum everywhere you might just as well place the enum outside.

          • Alex

            My opinion is if an enum is tied to the struct or class, placing the enum inside the struct or class is better than outside, as it makes it clearer that the two are bound together. Generally when I do this, I use enum instead of enum class though (since the class or struct provides a namespacing effect).

  • Aashish Bharadwaj

    Chpt4.cpp

    funciones.h

    monster.h

  • fhmadcode

    Hi Alex, can you help me explain the different between the following statements?
    Although they are both acceptable in complier, I guess type 1 is more efficient than type2 ?

    • Alex

      They should be identical -- both do aggregate initialization. #2 uses the classic style, whereas #1 uses the uniform initialization style (introduced in C++11).

  • greatTutorials(Alex);

    My code

    vs c++11 solution

    Is my solution okay, or in any way less efficient?
    Could printing the type like I did instead of returning a string be useful for something else in a program or would a string just do the same and more?

    Thanks

    • Alex

      If all you need to do is print the name, then having a function that just prints the name is probably more efficient than returning a string and having the caller do the print. It's just less reusable since the caller can do anything with a string, but a function that prints can only print.

      Short answer: Since you don't need the string anywhere else, your code is fine. :)

  • KP

    #include "stdafx.h"
    #include <iostream>
    #include <string>

    enum class MonsterType
    {
        OGRE,
        DRAGON,
        ORC,
        SPIDER,
        SLIME
    };

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

    std::string getMonsterType(Monster monster)
    {
        if (monster.type == MonsterType::OGRE)
            return "Ogre";
        if (monster.type == MonsterType::DRAGON)
            return "Dragon";
        if (monster.type == MonsterType::ORC)
            return "Orc";
        if (monster.type == MonsterType::SPIDER)
            return "Giant Spider";
        if (monster.type == MonsterType::SLIME)
            return "Slime";

        return "Unknown";
    }

    void printMonster(Monster monster)
    {
        std::cout << "This " << getMonsterType(monster) << " is named " << monster.name << " and has " << monster.health << "health\n";
    }

    int main()
    {
        Monster OGRE = { MonsterType::OGRE, "Trog", 145 };
        Monster SLIME = { MonsterType::SLIME, "Blurp", 24 };

        printMonster(OGRE);
        printMonster(SLIME);

        return 0;
    }

    My code is almost identical but am getting some errors related to convert MonsterType to a std::string.
    I'm using Visual Studio 2017.

    • Alex

      The issue is that order of the members of the struct Monster don't match the order in which you're initializing your members in OGRE and SLIME. You'll need to make them consistent.

  • Kushagra

    Can you please list the programs which can be made till these topics , so that we can practice them at home .

  • codenoob

    Hey!

    This is what I made up. Is it a lot worse than the one in your example? :)
    Thanks for the site btw, it's just great and I think I'm starting to get a hang of this a bit at a time.

    • Alex

      Structurally the program looks good, you're just doing a few things that are less efficient. First, you're unnecessarily casing enumerators of type MonsterType to integers, when that's not necessary. Second, you're returning string literals as std::string when all you're doing is printing them. Returning char* would avoid an unnecessary conversion.

      • codenoob

        Thanks. Haha, yeah I actually ran into an error that I couldn't understand and get past until I used the static casts. :) Now that I look at your example I see the problem was in my struct and all I tried fixing was in the getMonsterType function and the monster creation.
        But I guess realizing your own errors and how to fix them may be the hardest part. :D

  • lnm

    I completed the quiz. Please check it or correct it :D.

    I love your tutorial since i'm rushing it. Thanks.

  • antiriad7

    Why didn't you simply do like this:

  • c++ learner

    compiler shows that MonsterType is not a class or namespace

  • Vissarion

    std::cout << "This " << type(monster) << "is named " << monster.name << "and has " << monster.health << "health";
    In this line of code, my compiler produces an error "No operator "<<" matches these operands" and it refers to the second time I use them, between "this" and type(monster), what could be wrong?
    Thanks in advance

  • I am attempting to design a program using the information learned in this chapter, basically a simple math quiz that tells you how you did at the end. Here is the code:

    I get an error on line 7 that says 'error: invalid use of non-static data member UserInformation::userName'. What does this mean and how can I fix it? Thanks again for responding to our comments and making these awesome tutorials.

    • Alex

      UserInformation is a struct (which is a type) definition. This doesn't allocate any memory, so you can't use it to store values.

      To actually use the struct, you need to instantiate a variable of that type (which does allocate memory). Then you can use that variable to store and retrieve values.

      So basically, at the top of main, add:

      [code]
      UserInformation user; // allocate a variable of type UserInformation named user
      [code]

      And then anywhere you have "UserInformation::", replace it with "user.".

      • Thanks! I've got it mostly figured out now, except usually when I define a new variable of type 'auto' I get this error: "error: non-static data member declared 'auto'". I'd like to fix this so that I can make the percentCorrect variable work properly. I tested my program, purposely answered just 1 question correct, yet it said I got 0% correct instead of 33%.

        Here's my code right now:

  • Alesca

    Hi Alex. Thanks for the resource. Enjoying the tutorials.

    My compiler is raising an error related to my struct definition:

    The error: " Expected member name or ';' after declaration specifiers " pointed at the first line of the code 'struct...'

    Do you have any suggestions ?

    • Alex

      Hard to say without seeing the rest of the program. Most likely, something that came before this wasn't properly terminated with a semicolon.

      • Alesca

Leave a Comment

Put all code inside code tags: [code]your code here[/code]