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; }
- 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 keywordspublic
andprivate
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);
- 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 areference type
. A reference is simple an alternative name for an object, so reference type is a another world for object type. To define areference type
we have to use a&
. In our constructor we havestd::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 nowpass by reference
instead ofpass 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 = ""
- 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 asconstant reference
. This declaration informs the compiler that, even though the argument ispassed by reference
, its initial value can not change. So inside our constructor we haveconst 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 topass-by-reference
. Then we mark it withconst
to say that we do NOT want to change the original value. Lastly we setn = ""
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; }
- 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){}
- 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 aInitializer 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;
}
- 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
};
- The code above actually doesn't look too unfamiliar from GameEntry. However, it does have a few differences, like,
~Scores();
andthrow(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 todelete
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 thenew
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);
- 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;
}
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 thenew
operator is used to create a class, that class's constructor is called. Using thenew
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.
Top comments (0)