DEV Community

Cover image for Game setup in C++
Tristan Elliott
Tristan Elliott

Posted on

Game setup in C++

Introduction

  • This series is going to be dedicated to first understanding the basics of C++ syntax and then evolve into learning data structures and algorithms in C++. For this series I will be reading Data Structures and algorithms in C++ Fourth edition by Adam Drozek. The book is very expensive on Amazon by this is the internet, so I am sure you can find a cheaper PDF version.

Arrays

  • For this post we will be creating a system to represent storing entries in an array; in particular, high score entries for a video game. We will be creating two classes:

1) GameEntry
2) Scores

GameEntry

#include <iostream>

class GameEntry {
public:
    GameEntry(const std::string& n = "", int s = 0);

    std::string getName() const;
    int getScore() const;

private:
    std::string name;
    int score;
};
//End of class
GameEntry::GameEntry(const std::string& n, int s)
    :name(n),score(s){}

std::string GameEntry::getName() const { return name; }

int GameEntry::getScore() const { return score; }

Enter fullscreen mode Exit fullscreen mode
  • Now if you have used any sort of Object oriented programming language before, then this doesn't look too complicated. The keyword class is used to declare a class. The keywords public and private are used to declare the public and private members.

The constructor

  • So first lets talk about what a constructor is. A constructor is a method that has the same name as the class and it has no return type. For us the constructor is :
GameEntry(const std::string& n = "", int s = 0);
Enter fullscreen mode Exit fullscreen mode
  • Inside the constructor's argument list you may notice the weird usage of const and &. In order to understand those we need to first talk about Arguments in C++

Argument Passing

  • By default arguments in C++ are pass by value, this means that when arguments are passed to a method the system makes a copy of the actual argument and uses the copied value. This way no actual changes can be done to the initial value. What if we want to change the actual value of an argument we passed into a method? To do this we have to define our argument to be a reference type. A reference is simple an alternative name for an object, so reference type is a another world for object type. To define a reference type we have to use a &. In our constructor we have std::string& n, which is how we declare an argument to be a reference of type string called n. When we do this we are saying, the method is now pass by reference instead of pass by value. Now, pass by reference has some very important benefits, especially when it comes to memory. When we pass-by-reference the system does not have to create a whole new structure to pass to the method, thus saving space and memory.

Constant references as arguments

const std::string& n = ""
Enter fullscreen mode Exit fullscreen mode
  • The last thing that we have to try and identify is the const keyword. Well, since most function arguments are not modified, an even better practice is to pass arguments as constant reference. This declaration informs the compiler that, even though the argument is passed by reference, its initial value can not change. So inside our constructor we have const std::string& n = "" , which means: we have defined the method arugument list to take a argument called n, then marked it with & to notify the compiler that we want to pass-by-reference. Then we mark it with const to say that we do NOT want to change the original value. Lastly we set n = "" which is just a default value incase n does not get passed in during the class's instantiation.
  • The last part of the constructor is pretty straight forward, int s = 0. We define a argument of type int and assign it a default value of 0.

Method definitions

GameEntry::GameEntry(const std::string& n, int s)
    :name(n),score(s){}

std::string GameEntry::getName() const { return name; }

int GameEntry::getScore() const { return score; }
Enter fullscreen mode Exit fullscreen mode
  • This section of method declaration might seem a little odd but it is actually not too hard to understand. What we are doing above is defining methods outside of the class. Which provides a really clean class structure, it is very easy to understand what the class is doing in a abstract meaning. The only one strange part that might be hard to understand is the definition of the constructor:
GameEntry::GameEntry(const std::string& n, int s)
    :name(n),score(s){}
Enter fullscreen mode Exit fullscreen mode
  • First notice that we are able to define our GameEntry constructor outside of the class by preceding the function name with the scope resolution operator. The scope resolution operator is the ::. The other strangeness, :name(n),score(s){} is called a Initializer list

Initializer list

  • This list is placed between the constructor's argument list and it's body. It consists of a colon(:) followed by a comma-separated list, it is used as an alternative way to initialize member variables. An alternative to a Initializer list would be:
GameEntry::GameEntry(const std::string& n ="", int s)
    {
    name = n;
    score = s;
}
Enter fullscreen mode Exit fullscreen mode
  • As you can tell an Initializer list is really nothing more than a cleaner way to initialize member variables.

Scores

class Scores {
public:
    Scores(int maxEnt = 10);
    ~Scores();
    void add(const GameEntry& e);
    GameEntry remove(int i)
        throw(std::out_of_range);



private:
    int maxEntries;     //maximum number of entries
    int numEntries;     //actual number of entries
    GameEntry* entries; // array of game entries
};
Enter fullscreen mode Exit fullscreen mode
  • The code above actually doesn't look too unfamiliar from GameEntry. However, it does have a few differences, like, ~Scores(); and throw(std::out_of_range);. First lets talk about the ~Scores(); method and to do that we need to talk about destructors

Destructors

  • Where a constructor is called during an objects initialization. A destructor is a member method that gets called when an object ceases to exist. A destructor is denoted by the use of ~, so ~Scores(); is a destructor for the Scores class. A destructor is a member function that is invoked automatically when the object goes out of scope or is explicitly destroyed by a call to delete

  • If we do no define a destructor, the compiler will provide a default one for us. Destructors also help us avoid memory leaks, which are inaccessible blocks of memory that cannot be deleted. Anytime we use the new operator (which we are) it is important to also define a constructor.

Exceptions

  • The next bit of odd looking code is:
GameEntry remove(int i)
        throw(std::out_of_range);
Enter fullscreen mode Exit fullscreen mode
  • The keyword throw tells us that we are dealing with an exception.

What is an exception?

  • Exceptions are unexpected events that occur during the execution of a program. An exception can be the result of an error condition or simply an unexpected input.
  • In the code that is pasted above, the throw keyword lets us and the compiler know what kind of exception might occur. So with the throw keyword our remove method is saying, provide me with an int argument, I might encounter a out_of_range exception but make sure I return a GameEntry object. More on exceptions and the remove method in the next blog post.

Method definitions

Scores::Scores(int maxEnt) {
    maxEntries = maxEnt;
    entries = new GameEntry[maxEntries]; 

    numEntries = 0;
};
Scores::~Scores() {
    delete[] entries;
}
Enter fullscreen mode Exit fullscreen mode
  • As you might of noticed we are defining the member methods with the scope resolution operator, which gives us access to different scopes. The next bit of code that you should draw your eye to is the new keyword. It specifically allocates memory for an object or array of objects from the free store(memory we can store things in) and returns a pointer to the object. When the new operator is used to create a class, that class's constructor is called. Using the new operator is primary reason that we have to create a destructor.

  • More on add() and remove() in the next post.

Conclusion

  • Thank you for taking the time out of your day to read this blog post of mine. If you have any questions or concerns please comment below or reach out to me on Twitter.

Discussion (0)