Search

8.x — Chapter 8 comprehensive quiz

In this chapter, we explored the meat of C++ -- object-oriented programming! This is the most important chapter in the tutorial series.

Chapter summary

Classes allow you to create your own data types that bundle both data and functions that work on that data. Data and functions inside the class are called members. Members of the class are selected by using the . operator (or -> if you’re accessing the member through a pointer).

Access specifiers allow you to specify who can access the members of a class. Public members can be accessed directly by anybody. Private members can only be accessed by other members of the class. We’ll cover protected members later, when we get to inheritance. By default, all members of a class are private and all members of a struct are public.

Encapsulation is the process of making all of your member data private, so it can not be accessed directly. This helps protect your class from misuse.

Constructors are a special type of member function that allow you to initialize objects of your class. A constructor that takes no parameters (or has all default parameters) is called a default constructor. The default constructor is used if no initialization values are provided by the user. You should always provide at least one constructor for your classes.

Member initializer lists allows you to initialize your member variables from within a constructor (rather than assigning the member variables values).

In C++11, non-static member initialization allows you to directly specify default values for member variables when they are declared.

Prior to C++11, constructors should not call other constructors (it will compile, but will not work as you expect). In C++11, constructors are allowed to call other constructors (called delegating constructors, or constructor chaining).

Destructors are another type of special member function that allow your class to clean up after itself. Any kind of deallocation or shutdown routines should be executed from here.

All member functions have a hidden *this pointer that points at the class object being modified. Most of the time you will not need to access this pointer directly. But you can if you need to.

It is good programming style to put your class definitions in a header file of the same name as the class, and define your class functions in a .cpp file of the same name as the class. This also helps avoid circular dependencies.

Member functions can (and should) be made const if they do not modify the state of the class. Const class objects can only call const member functions.

Static member variables are shared among all objects of the class. Although they can be accessed from a class object, they can also be accessed directly via the scope resolution operator.

Similarly, static member functions are member functions that have no *this pointer. They can only access static member variables.

Friend functions are functions that are treated like member functions of the class (and thus can access a class’s private data directly). Friend classes are classes where all members of the class are considered friend functions.

It’s possible to create anonymous class objects for the purpose of evaluation in an expression, or passing or returning a value.

You can also nest types within a class. This is often used with enums related to the class, but can be done with other types (including other classes) if desired.

Quiz time

1a) Write a class named Point2d. Point2d should contain two member variables of type double: m_x, and m_y, both defaulted to 0.0. Provide a constructor and a print function.

The following program should run:

This should print:

Point2d(0, 0);
Point2d(3, 4);

Show Solution

1b) Now add a member function named distanceTo that takes another Point2d as a parameter, and calculates the distance between them. Given two points (x1, y1) and (x2, y2), the distance between them can be calculated as sqrt((x1 - x2)*(x1 - x2) + (y1 - y2)*(y1 - y2)). The sqrt function lives in header cmath.

The following program should run:

This should print:

Point2d(0, 0);
Point2d(3, 4);
Distance between two points: 5

Show Solution

1c) Change function distanceTo from a member function to a non-member friend function that takes two Points as parameters. Also rename it “distanceFrom”.

The following program should run:

This should print:

Point2d(0, 0);
Point2d(3, 4);
Distance between two points: 5

Show Solution

2) Write a destructor for this class:

Show Solution

3) Let’s create a random monster generator. This one should be fun.

3a) First, let’s create an enumeration of monster types named MonsterType. Include the following monster types: Dragon, Goblin, Ogre, Orc, Skeleton, Troll, Vampire, and Zombie. Add an additional MAX_MONSTER_TYPES enum so we can count how many enumerators there are.

Show Solution

3b) Now, let’s create our Monster class. Our Monster will have 4 attributes (member variables): a type (MonsterType), a name (std::string), a roar (std::string), and the number of hit points (int). Create a Monster class that has these 4 member variables.

Show Solution

3c) enum MonsterType is specific to Monster, so move the enum inside the class as a public declaration.

Show Solution

3d) Create a constructor that allows you to initialize all of the member variables.

The following program should compile:

Show Solution

3e) Now we want to be able to print our monster so we can validate it’s correct. To do that, we’re going to need to write a function that converts a MonsterType into a std::string. Write that function (called getTypeString()), as well as a print() member function.

The following program should compile:

and print:

Bones the skeleton has 4 hit points and says *rattle*

Show Solution

3f) Now we can create a random monster generator. Let’s consider how our MonsterGenerator class will work. Ideally, we’ll ask it to give us a Monster, and it will create a random one for us. We don’t need more than one MonsterGenerator. This is a good candidate for a static class (one in which all functions are static). Create an empty MonsterGenerator class. Create a static function named generateMonster(). This should return a Monster. For now, make it return anonymous Monster(Monster::Skeleton, “Bones”, “*rattle*”, 4);

The following program should compile:

and print:

Bones the skeleton has 4 hit points and says *rattle*

Show Solution

3g) Now, MonsterGenerator needs to generate some random attributes. To do that, we’ll need to make use of this handy function:

However, because MonsterGenerator relies directly on this function, let’s put it inside the class, as a static function.

Show Solution

3h) Now edit function generateMonster() to generate a random MonsterType (between 0 and Monster::MAX_MONSTER_TYPES-1) and a random hit points (between 1 and 100). This should be fairly straightforward. Once you’ve done that, define two static fixed arrays of size 6 inside the function (named s_names and s_roars) and initialize them with 6 names and 6 sounds of your choice. Pick a random name from these arrays.

The following program should compile:

Show Solution

3i) Why did we declare variables s_names and s_roars as static?

Show Solution

4) Okay, time for that game face again. This one is going to be a challenge. Let’s rewrite the Blackjack games we wrote in chapter 6 using classes! Here’s the full code without classes:

Holy moly! Where do we even begin? Don’t worry, we can do this, but we’ll need a strategy here. This Blackjack program is really composed of four parts: the logic that deals with cards, the logic that deals with the deck of cards, the logic that deals with dealing cards from the deck, and the game logic. Our strategy will be to work on each of these pieces individually, testing each part with a small test program as we go. That way, instead of trying to convert the entire program in one go, we can do it in 4 testable parts.

Start by copying the original program into your IDE, and then commenting out everything except the #include lines.

4a) Let’s start by making Card a class instead of a struct. The good news is that the Card class is pretty similar to the Monster class from the previous quiz question. First, move the enums for CardSuit, CardRank inside the card class as public definitions (they’re intrinsically related to Card, so it makes more sense for them to be inside the class, not outside). Second, create private members to hold the CardRank and CardSuit (name them m_rank and m_suit accordingly). Third, create a public constructor for the Card class so we can initialize Cards. Forth, make sure to assign default values to the parameters so this can be used as a default constructor (pick any values you like). Finally, move the printCard() and getCardValue() functions inside the class as public members (remember to make them const!).

Important note: When using a std::array (or std::vector) where the elements are a class type, your element’s class must have a default constructor so the elements can be initialized to a reasonable default state. If you do not provide one, you’ll get a cryptic error about attempting to reference a deleted function.

The following test program should compile:

Show Solution

4b) Okay, now let’s work on a Deck class. The deck needs to hold 52 cards, so use a private std::array member to create a fixed array of 52 cards named m_deck. Second, create a constructor that takes no parameters and initializes m_deck with one of each card (modify the code from the original main() function). Inside the initialization loop, create an anonymous Card object and assign it to your deck element. Third, move printDeck into the Deck class as a public member. Fourth, move getRandomNumber() and swapCard() into the Deck class as a private static members (they’re just helper functions, so they don’t need access to *this). Fifth, move shuffleDeck into the class as a public member.

Hint: The trickiest part of this step is initializing the deck using the modified code from the original main() function. The following line shows how to do that.

The following test program should compile:

Show Solution

4c) Now we need a way to keep track of which card is next to be dealt (in the original program, this is what cardptr was for). First, add a int member named m_cardIndex and initialize it to 0. Create a public member function named dealCard(), which should return a const reference to the current card and advance the index. shuffleDeck() should also be updated to reset m_cardIndex (since if you shuffle the deck, you’ll start dealing from the top of the deck again).

The following test program should compile:

Show Solution

4d) Almost there! Now, just fix up the remaining program to use the classes you wrote above. Since most of the initialization routines has been moved into the classes, you can jettison them.

Show Solution

9.1 -- Introduction to operator overloading
Index
8.15 -- Nested types in classes

149 comments to 8.x — Chapter 8 comprehensive quiz

  • Hardik

    Don’t You Think We should use an anonymous object here?
    Better, No?

    • Alex

      I’m not sure what you mean. All you’ve done is remove the temporary variable. There are no anonymous objects here, and I’m not sure why we’d need one.

      • Hardik

        My ultimate suggestion was that it is good to prevent the creation of a temporary variable !
        As you also say, we shouldn’t create unnecessary variables 🙂 !

        • Alex

          > My ultimate suggestion was that it is good to prevent the creation of a temporary variable !

          It’s best to do whatever is clearest for code comprehension. Temporary variables can help document what your code is doing, making it easier to understand. They also make your code easier to debug since you can see intermediate values. The compiler will probably optimize temporary variables away anyway.

          I probably wouldn’t create a variable called “sum” just to add two integers together (doesn’t really add value) -- but I would create a temporary variable to hold the result of a function call before I do something else with it.

  • Interesting, I did not know you could have multiple public or private sections in a class.

    And in problem 3d, what is the point of the empty set of curly brackets?

    • Alex

      The Monster constructor does everything it needs to do through the member initializer list, so the body of the constructor is empty. This is common for constructors that just do pure initialization.

  • Omri

    Hello Alex,
    Regarding:
    "It is good programming style to put your class *declarations* in a header file of the same name as the class, and define your class functions in…"
    Consider:
    "It is good programming style to put your class *definitions* in a header file of the same name as the class, and define your class functions in…"

  • Felipe

    Hello Alex, on 3f-3h quiz wouldn’t it be better to include srand() and rand() inside the function since they need to be used every time anyways? is there a reason why this should not be done or is it just preference?

    • Alex

      srand() should only be called once per program, otherwise the results will not be suitably random. If you put it inside the function, it will get called every time.
      Discarding a random result with each call could, in theory, result in a skewed random distribution… but more likely, it will just result in wasted performance.

      So, no, you shouldn’t do this.

  • Felipe

    In Quiz 2:

    In a normal function the *m_data pointer should be set to a null pointer once deleted?,

    , but inside a class do you need to do so also or you don’t since it’s a private pointer? Thanks for the great work.

    • Alex

      Generally speaking, you should set your pointers to nullptr after deleting them. However, the one exception is for deleted pointers inside a destructor. Because the entire class object will be destroyed immediately after the destructor finishes, in most cases you don’t need to set your pointers to nullptr since they’ll be destroyed subsequently anyway.

  • Ethan Hulinsky

    Try this

  • Satwant

    A class BlackJack for playBlackJack also.

  • Satwant

    can we create s_names and s_roars as class static member variables?

    • Alex

      You can, but it’s generally better to define variables in the smallest scope possible. Since generateMonster() is the only user of those fields, it’s better to put it inside generateMonster() rather than at the class level.

  • john

    Alex, why are getRandomNumber and swapCard in the deck class static functions?

  • AS

    I understand that to use a structure/class before it is completely defined(declared), we cannot refer to it directly. But we can use pointer to it.
    In solution 1(b), the declaration of distance() is taking the parameter of type "class Point2d". i.e. it is taking parameter of class type whose definition is still incomplete.

    Code compiles and works but How does it work? Shouldn’t it give a compilation error ?

    • Alex

      Member variables must be complete. However, function parameters are allowed to be incomplete, so long as they have been declared. Without this, writing a copy constructor or assignment operator would be impossible.

      • AS

        Okay, even then the declaration of Point2d is not complete when it was used as parameter to distance(Point2d) ?
        Does it require advanced declaration like “class Point2d ; ” ?

  • Aakash

    Hello Alex,
    How can I define a variable in the class which is only modificable by constructor but not outside the class. The variable must be read-only outside the class.

    • Alex

      Make the variable a const member variable and ensure it is private. Making it const will mean it’s only initializable by the constructor, but not after that. And making it private will mean only members of the class can access it. If you want outsiders to be able to access it, use an access function.

      • Aakash

        What I want is that it must be read-only outside the class but cannot be changed. Inside the class, any function can change its value. For eg, suppose I create my own string class. It contains an integer value Length. That value(Length) cannot be made const because it changes Everytime I use += operator, = operator, it’s length changes every time but it cannot be modified outside the class. But outside the class, I must have access to it(like const) so that the following line compiles fine:

        • Alex

          It is not possible to have a member that meets all of the following criteria:
          * Is directly accessible outside the class (is public)
          * Is modifiable within the class
          * Is not modifiable outside the class

  • Michiel

    Hi Alex! I got a question on exercise 3h. My code keeps generating Orcs, but name, hitpoints and roar are always random
    Here’s my code:

    EDIT: I changed the type generation line from this

    to this

    Now it only generates Skeletons…

    • Alex

      Many compilers have a bug where the first random number they generate after calling srand() actually isn’t random. You likely can fix your program by calling rand() once (just after the call to srand()) and ignoring the result.

  • Anil

    I can’t under this:

  • Mohammad

    hello, now that iv learned about classes i dont see the point of struct. is there an advantage of using a struct over a class?

    Also thanks for the great quiz really helpful!

    • Alex

      Structs can still be useful when you want to package up several different data types into a single container, but you want to treat them as just a data collection, not as an object with behaviors.

  • Christopher

    Hello! As I was working on question 3h), why would we refer to MAX_MONSTER_TYPES by doing Monster::MAX_MONSTER_TYPES? That was the only part I kinda had some trouble on. Maybe it was because I just forgot how enums worked.

    • Alex

      Enumerators are put in the same scope as the enumeration. In the case where the enum is defined inside a class, the scope of the enum becomes the class, so you need to use the class name as a prefix to access the enumerator.

      If we’d left MonsterType outside the class, you wouldn’t need the Monster:: prefix.

  • Hugh Mungus

    Hey Alex/Watermelon Cat,

    Doing the 4th question, I have encountered a strange error!

    Looking at stackexchange for help, it’s said that the compiler is implicitly deleting my constructor. How can I stop it from doing that?

    • Alex

      When a std::array is created, it needs access to the default constructor of the element type to initialize the array elements to their default state. Does your Card class have a default constructor? If not, add one.

      • Hugh Mungus

        Oh, thanks. I did not have a default constructor. I didn’t think to look at the card class since it threw an error at the deck class.

  • Aagami

    Hey Alex! Maybe I am getting it wrong. But in 1(b), why is it that passing second as an argument to other allows its private members to be accessed via a dot operator only?
    return sqrt((m_x - other.m_x)*(m_x - other.m_x) + (m_y - other.m_y)*(m_y - other.m_y));
        }

    • Alex

      Good question. This is because access specifiers work on a per-class basis, not a per-object basis.

      Because distanceTo is a member of Point2d, it has access to the private members of ANY Point2d object it can access. In this case, this includes not only the implicit object (this), but also function parameter “other”!

  • Gapo

    Hey Alex, do I need to memorize syntax for random number generator, or should I better ask if every software engeneer knows all syntax by heart ?

  • JoePerkins

    Any chance to get the solutions separated by cpp/h files for each class? I find it hard to see where to put the includes (I always think I’m duplicating them unnecessarily), and I also get a weird Visual Studio Community 2015 error C2280 in the deck exercise:
    >deck.cpp(10): error C2280: ‘std::array<Card,52>::array(void)’: attempting to reference a deleted function

    I have Deck.h:

    and Deck.cpp:

    If having separate files is the normal way to do things, I think it would be nice if this long quizzes offered the solution in that form.

    Thanks! 🙂

  • Surya

    my generateMonster function generates everything except the monster type randomly, but the type doesn’t change at all - prints one type again and again.
    this is my code for the function. Pls help me fix it. thanks 🙂

    PS: i use gcc 5.1

    • Surya

      well doing this after initialisation of type fixed it
      type = static_cast<Monster::MonsterType>(getRandomNumber(0, Monster::MAX_MONSTER_TYPE - 1));

      but why didn’t the problem happen to rest of the variables why just the enumerated variable?

  • Maese

    First of all, thank you for these great tutorials. I always wanted to learn C++ and I am finding your tutorials the perfect balance for my learning speed.
    I have one question on 1b). In the current posted solution the distanceTo() function is a public member of the Point2d class. It is not declared as friend (not at 1b).
    Inside that function the code reads other.m_x, where ‘other’ is a Point2d argument, i.e. an object of class Point2d. How can we possibly access the m_x member of object ‘other’, considering that m_x is a private member?

    • Alex

      This is something that a lot of people miss or mistake -- access controls work on a per-class basis, not a per-object basis. Because distanceTo() is a member function of the Point2d class, it has access not only to the private members of the implicit this object, it also has access to the private members of any other object of type Point2d that it has access to (which in this case is parameter other). I’ve updated the lesson on access controls accordingly.

      • Maese

        Thanks Alex. It is clear now after reading your answer and your update to 8.3
        When I tried to do 1b I first used an access function getX() and getY() because I thought that was the only way to get it working and stay "encapsulated". Now that I understand this "per-class basis" behaviour it makes lots of sense, precisely to avoid the need of these extra access functions.
        By the way, I think there are some formatting issues with that update. Some of the explanation text is formatted as the code pieces.

  • Matt

    In quiz #2, shouldn’t the destructor delete the memory allocated by the constructor?

  • Rose

    Hi Alex,

    I tried to answer question 4 first on my own, and ended up with 3 classes and header files with matching source files (Card.cpp, Deck.cpp, Game.cpp)

    My main.cpp code:

    #include <iostream>
    #include "Deck.h"
    #include "Game.h"

    int main()
    {
    Deck myDeck;

    for (;;)
        {
        Game::prepare(myDeck);

            if (Game::playBlackjack(myDeck))
                    std::cout << "You win!n";
            else
                    std::cout << "You lose!n";

            if (!Game::restartGame())
            {
                break;
            }
        }

    return 0;
    }

    All 3 Game functions are static,
    and "Game:prepare(Deck& deck)" consists of "m_deck.shuffle()" and "srand()".

    How efficient/inefficient do you think this code is?
    I ask because I noticed your solution didn’t include any custom header files…

    (PS- I am not sure how to use the

    )

    • Alex

      Seems fine to me. I didn’t use multiple files just to keep the solution code all in one place, but if I’d been writing my own code, I would have split them like you did. Doesn’t seem inefficient at all, and the structure of your main function is nice and straightforward.

  • Ninja

    I tried doing the blackjack game by having each class in seperate .h and .cpp files but I’m running into issues mainly with my functions not being detected in other classes. Even when I make them a friend, it doesn’t work. For example, I can’t use the printCard function in my Deck class so I ended up making the printCard function static. So I didn’t follow the instructions because of those issues but my game still works.

    Also, for question 3i, does making it static in this case mean the array is not destroyed when it goes out of scope? I mean do you mean static like how its used in section 4.3 and not the static definition when it comes to classes?

    • Alex

      For 3i, those variables will get initialized at program startup and destroyed when the program terminates. They do not get destroyed when they go out of scope. So yes, these are exactly the use case that’s talked about in lesson 4.3, not static member variables.

      As for why you can’t call your function, you probably need forward declarations for your functions so the other files know they exist. You can either do that manually (by adding forward declarations to the top of the files that need them), or by putting the forward declarations in a header file and including the header (better).

  • Leo

    Hi Alex, I have a question on 3h) I tried writing my code and it was similar to yours with the exception that I declared my s_roar[6] and s_name[6] outside of my generateMonster() function.

    I got an error in VisualStudio, error C2864 stating that "a static data member with an in-class initializer must have non-volatile const integral type" after some googling it seems that volatile values are ones that can be modified by outside sources. Since I made s_name and s_roar static and there are no functions that change it how is it considered volatile?

    The problem was fixed as soon as I moved s_roar and s_name inside the generateMonster() function, then it compiled just fine and I was able to run it. What exactly was the change that made it functional?

    • Alex

      The issue here isn’t that you defined s_roar and s_name as static members, but that you tried to initialize them directly. Static members of a class can only be _directly_ initialized with integral values or enumerated values. For initialization with other values, you need to do that outside of the class, like so:

      This was mentioned in the lesson on static members, but upon reflection it’s maybe not obvious enough. I’ll put it in its own subsection, so it stands out a little more.

      • Kayla

        In your solution above, you initialized s_roar and s_name within the generateMonster function, which is still within the MonsterGenerator class. in the static member variables/functions lessons, you initialized static members completely outside the class. Which of these practices is correct?

        • Alex

          They’re both correct. s_roar and s_name are local static variables. They’re initialized inside the function to keep the scope limited to that function. Local static variables are initialized wherever they are defined.

          In the lesson on static members, they’re initialized outside the class because static members must be initialized outside the class, in the global scope. This is necessary because that static value is accessible anywhere (even without an object of the class).

  • Tomas

    I believe this code would be way simplier to understand instead of yours. 🙂

    Anyway, great job with these tutorials. They are the best i could find in the internet and i did check a looot!

    • Alex

      Easier, yes, but also flawed. Your suggested code produces an uneven distribution, where low numbers may be generated more often than high numbers. If min is 0 and max is 10000, for example, the numbers between 0 and 2767 will be generated 4 times for every 3 times the numbers between 2768 and 9999 are generated.

  • Damien

    Hey Alex,

    Great tutorial, I love it! Keep it up! 🙂

    I’m running into some issues with the MonsterGenerator class. Considering what you said on enumerations, I thought it would be better to use an enum class to implement the MonsterType. My code compiles just fine and seems to be working but after a few tests, it seems that I always end up with the same Monster Type. Maybe the fact it is an enum class is related to that problem, that is just my guess. Did I do anything wrong?

    Moreover, in that particular case, using an enum class seems to add a little bit more work as you need to prefix the monster’s type with "Monster::MonsterType::". This is just as well annoying as you need to use a static_cast to explicitly use it as an int. Considering that enum classes are useful when it comes to limit the risks of name duplication, I guess you used a simple enum because the scope is already limited to the Monster class, am I right? Should we consider that enum classes should always be used as stand-alone, whereas enum within classes should just be enums?

    Anyway, here is a snippet of my code, thank you in advance for your answers:

    • Alex

      Enum classes are better if you are defining your enumerated type in the global scope. Enums are fine if you’re defining your enumerated type inside a class (because the enumerators will be contained to the class).

      As for why all your monsters are zombies, it’s because of a bug in Visual Studio’s (and some other compiler’s) random number generator. After srand(), call rand() once to discard the first value, and subsequent values will randomize as expected.

      • Damien

        Thank you for the quick answer Alex!

        Yeah that’s what I thought. I even found this link which may be a nice addition to your answer on enums in classes: http://stackoverflow.com/questions/27044055/c-enum-inside-of-a-class-using-enum-class/27044158

        Wow, that’s weird but this seems to be the issue indeed, calling rand() once to discard the first value actually did the trick. I was aware that it was a problem with Visual Studio, but I would not expect it to bug with Clang (I am actually compiling in CLI). From now on, I’ll always add that call just in case.

        Anyway, thanks again.

        • Alex

          The point about type safety is relevant one -- enums don’t provide type safety (they convert freely amongst each other), so if this is a concern, enum classes is a better choice. I don’t like putting enums related to a class outside the class just to avoid typing a scoping prefix.

  • Matt

    Alex,

    I’ve finished updating my blackjack program using classes but since my program is much different than yours, I have a couple of questions.

    1) I decided to put the call to srand() inside the constructor of my Deck class, just to keep it out of main(). Does this make sense, or is there a downside to this?

    2) I decided to create a Blackjack class. I’m not sure whether this was a good idea or not, and would like your opinion on it. It’s private member variables/functions are:
            int player_wins
            int dealer_wins
            Deck play_deck
            enum Over             //to enumerate the 7 different ways the dealer/player can win

            void tallyScore()     //to update the score of the player/dealer
            void endGame(Over)    //to print out one of 7 win/lose messages, and update player_wins or dealer_wins

    It’s public functions are "menu()" and "play()". And I created a Blackjack object(blackjack) at the end.

    This is what’s in main():

       while(blackjack.menu())
           blackjack.play();
       return 0;

    I like how main() is so clean, but I’m not sure if it’s proper programming style to implement the blackjack game within a class, rather than keeping it in main.

    • Alex

      1) It’s better to put it in main(). For this program, it doesn’t matter so much, but consider a program where you were using more than on Deck of cards, or where you temporarily created a Deck. In either of those cases, srand() would get executed more than once, which is bad. You also want to make sure srand() gets executed at the start of your program, not when you create your first Deck.
      2) It’s fine to put everything inside a class if that helps you organize.

  • Christos

    Hi Alex,

    Seven text lines before ‘Quiz Time’, "Static member variables are shared among all members of the class.".

    I think that you want to write "…all objects of the class."