In Chapter 4: Loops, Arrays, Switch, Enumerations, and Functions - Implementing Game Mechanics, we talked about scope. The concept that variables declared in a function or inner block of code only have scope (can be seen or used) in that function or block. Using only the C++ knowledge we have at the moment, this can cause a problem. What do we do if we need to work on a number of complex objects which are needed in main
? This could imply that all the code must be in main
.
In this chapter we will explore C++references which allow us to work on variables and objects that are otherwise out of scope. In addition, references will help us avoid having to pass large objects between functions, which is a slow process. It is a slow process because each time we do this, a copy of the variable or object must be made.
Armed with this new knowledge about references, we will take a look at the SFML VertexArray
class that allows us to build up a large image that can be very quickly and efficiently drawn to the screen using multiple images from a single image file. By the end of the chapter we will have a scaleable, random, scrolling background, using references, and a VertexArray
object.
We will now talk about the following topics:
When we pass values to a function or return values from a function, that is exactly what we are doing. Passing/returning by value. What happens is that a copy of the value held by the variable is made, and sent into the function where it is used.
The significance of this is two-fold:
int
or even perhaps a sprite, this is fairly insignificant. However, for a complex object, perhaps an entire game world (or background), the copying process will seriously affect our game's performance.References are the solution to these two problems. A reference is a special type of variable. A reference refers to another variable. An example will be useful:
int numZombies = 100; int& rNumZombies = numZombies;
In the code above we declare and initialize a regular int
called numZombies
. We then declare and initialize an int
reference called rNumZombies
. The reference operator &
that follows the type, determines that a reference is being declared.
Now we have an int
called numZombies
which stores the value 100
and an int
reference called rNumZombies
that refers to numZombies
.
Anything we do to numZombies
can be seen through rNumZombies
, and anything we do to rNumZombies
we are actually doing to numZombies
. Take a look at the following code:
int score = 10; int& rScore = score; score++; rScore++;
In the previous code we declare an int
called score
. Next we declare an int
reference called rScore
that refers to score
. Remember that anything we do to score
can be seen by rScore
and anything we do to rScore
is actually being done to score
.
Therefore, when we increment score like this:
score++;
The score variable now stores the value 11. In addition, if we were to output rScore
it would also output 11. The following line of code is as follows:
rScore++;
Now score
actually holds the value 12 because anything we do to rScore
is actually done to score
.
If you want to know how this works then more will be revealed in the next chapter when we discuss pointers. But simply put, you can consider a reference as storing a place/address in the computer's memory. That place in memory is the same place where the variable it refers to stores its value. Therefore, an operation on either the reference or the variable has exactly the same effect.
For now, it is much more important to talk more about the why of references. There are two reasons to use references and we have already mentioned them. Here they are summarized again:
Study this code and then we can discuss it:
void add(int n1, int n2, int a); void referenceAdd(int n1, int n2, int& a); int main() { int number1 = 2; int number2 = 2; int answer = 0; add(number1, number2, answer); // answer still equals zero because it is passed as a copy // Nothing happens to answer in the scope of main referenceAdd(number1, number2, answer); // Now answer equals 4 because it was passed by reference // When the referenceAdd funtion did this: // answer = num1 + num 2; // It is actually changing the value stored by a return 0; } // Here are the two function definitions // They are exactly the same except that // the second passes a reference to a add(int n1, int n2, int a) { a = n1 + n2; // a now equals 4 // But when the function returns a is lost forever } referenceAdd(int n1, int n2, int& a) { a = n1 + n2; // a now equals 4 // But a is a reference! // So it is actually answer, back in main, that equals 4 }
The previous code begins with the prototypes of two functions, add
and referenceAdd
. The add
function takes three int
variables and the referenceAdd
function takes two int
variables and an int
reference.
When the add
function is called and the variables number1
, number2
, and answer
are passed in, a copy of the values is made and new local variables to add (n1
, n2
, and a
) are manipulated. As a result of this, answer
, back in main
, remains at zero.
When the referenceAdd
function is called, number1
and number2
are again passed by value. However, answer
is passed by reference. When the value of n1
added to n2
is assigned to the reference a
, what is really happening is that the value is assigned to answer
back in the main
function.
It is probably obvious that we would never need to actually use a reference for something this simple. It does, however, demonstrate the mechanics of passing by reference.
The previous code demonstrated how a reference can be used to alter the value of a variable in one scope using code in another. As well as being extremely convenient, passing by reference is also very efficient because no copy is made. The example using a reference to an int
is a bit ambiguous because as an int
is so small there is no real efficiency gain. Later in the chapter we will use a reference to pass an entire level layout and the efficiency gain will be significant.
There is one gotcha with references! You must assign the reference to a variable at the time you create it. This means it is not completely flexible. Don't worry about this for now. We will explore references further as well as their more flexible (and slightly more complicated) relations, pointers, in the next chapter.
This is largely irrelevant for an int
but potentially significant for a large object of a class. We will use this exact technique when we implement the scrolling background of the Zombie Arena game.
3.137.176.166