Getting started with functions

So what exactly are C++ functions? A function is a collection of variables, expressions, and control flow statements (loops and branches). In fact, any of the code we have learnt about in the book so far can be used in a function. The first part of a function that we write is called the signature. Here is an example function signature:

public void bombPlayer(int power, int direction) 

If you add an opening and closing pair of curly braces {…} with some code that the function actually performs then we have a complete function, a definition:

void shootLazers(int power, int direction) 
{ 
    // ZAPP! 
} 

We could then use our new function in another part of our code, as follows:

// Attack the player 
bombPlayer(50, 180) // Run the code in the function 
//  I'm back again - code continues here after the function ends 

When we use a function we say that we call it. At the point where we call bombPlayer, our program's execution branches to the code contained within that function. The function will run until it reaches the end or is told to return. Then the code will continue running from the first line after the function call. We have already been using the functions that SFML provides. What is different here is that we will learn to write and call our own functions.

Here is another example of a function, complete with the code to make the function return to the code that called it:

int addAToB(int a, int b) 
{ 
   int answer = a + b; 
   return answer; 
} 

The call to use the above function could look like the following:

int myAnswer = addAToB(2, 4); 

Obviously, we don't need to write functions to add two variables together, but the example helps us see a little further into the workings of functions. First we pass in the values 2 and 4. In the function signature the value 2 is assigned to int a, and the value 4 is assigned to int b.

Within the function body, the variables a and b are added together and used to initialize the new variable int answer. The line return answer; does just that. It returns the value stored in answer to the calling code, causing myAnswer to be initialized with the value 6.

Notice that each of the function signatures in the examples above varies a little. The reason for this is that the C++ function signature is quite flexible, allowing us to build exactly the functions we require.

Exactly how the function signature defines how the function must be called and if/how the function must return a value, deserves further discussion. Let's give each part of that signature a name so we can break it into parts and learn about them.

Here is a function signature with its parts described by their formal, technical terms:

return type | name of function | (parameters)

And here are a few examples we can use for each of those parts:

  • Return-type: bool, float, int and so on, or any C++ type or expression
  • Name of function: bombPlayer, shootLazers, setCoordinates, addAToB and so on
  • Parameters: (int number, bool hitDetected), (int x, int y) (float a, float b)

Now let's look at each part in turn.

Function return types

The return type, as the name suggests, is the type of the value that will be returned from the function to the calling code:

int addAToB(int a, int b)
{
    int answer = a + b; 
    return answer; 
} 

In our slightly dull, but useful, addAtoB example previously, the return type in the signature is int. The function addAToB sends back, or returns, to the code that called it, a value that will fit in an int variable. The return type can be any C++ type we have seen so far, or one of the ones we haven't seen yet.

A function does not have to return a value at all however. In this case the signature must use the void keyword as the return type. When the void keyword is used, the function body must not attempt to return a value as this will cause an error. It can, however, use the return keyword without a value. Here are some combinations of return type and the use of the return keyword that are valid:

void doWhatever()
{ 
  
    // our code 
    // I'm done going back to calling code here 
    // no return is necessary 
  
} 

Another possibility is as follows:

void doSomethigCool()
{ 
  
   // our code 
  
   // I can do this as long as I don't try and add a value 
   return; 
} 

The following code gives us yet more examples of possible functions. Be sure to read the comments as well as the code:

void doYetAnotherThing()
{ 
   // some code 
  
   if(someCondition)
   { 
  
      // if someCondition is true returning to calling code 
      // before the end of the function body 
      return; 
   } 
  
   // More code that might or might not get executed 
  
   return; 
  
   // As I'm at the bottom of the function body 
   // and the return type is void, I'm 
   // really not necessary but I suppose I make it 
   // clear that the function is over. 
 } 

bool detectCollision(Ship a, Ship b)
{ 
  
   // Detect if collision has occurred 
   if(collision) 
   { 
      // Bam!!! 
      return true; 
   } 
   else 
   { 
      // Missed 
      return false; 
   } 
  
} 

The last function example above, detectCollision is a glimpse into the near future of our C++ code, and demonstrates that we can also pass in  user-defined types, called objects, into functions to perform calculations on them.

We could call each of the functions above, in turn, like this:

// OK time to call some functions 
doWhatever(); 
doSomethingCool(); 
doYetAnotherThing(); 
  
if (detectCollision(milleniumFalcon, lukesXWing)) 
{ 
   // The jedi are doomed! 
   // But there is always Leia. 
   // Unless she was on the Falcon? 
} 
else 
{ 
   // Live to fight another day 
} 
  
//continue with code from here 

Don't worry about the odd-looking syntax regarding the detectCollision function, we will see real code like this, quite soon. Simply, we are using the return value (true or false) as the expression, directly in an if statement.

Function names

The function name, when we design our own function, can be almost anything at all. But it is best to use words, usually verbs, to clearly explain what the function will do. For example, look at this function:

void functionaroonieboonie(int blibbityblob, float floppyfloatything) 
{ 
   //code here 
} 

The above is perfectly legal, and will work, but these next function names are much clearer:

void doSomeVerySpecificTask() 
{ 
   //code here 
} 

void getMySpaceShipHealth() 
{ 
   //code here 
} 

void startNewGame() 
{ 
   //code here 
} 

Next, let's take a closer look at how we share some values with a function.

Function parameters

We know that a function can return a result to the calling code. What if we need to share some data values from the calling code with the function? Parameters allow us to share values with the function. We have actually already seen examples of parameters while looking at return types. We will look at the same example but a little more closely:

int addAToB(int a, int b) 
{ 
  
   int answer = a + b; 
   return answer; 
  
} 

Above, the parameters are int a and int b. Notice that, in the first line of the function body, we use a + b as if they are already declared and initialized variables. Well, that's because they are. The parameters in the function signature are their declaration and the code that calls the function initializes them.

Tip

Important jargon note

Notice that we are referring to the variables in the function signature brackets (int a, int b) as parameters. When we pass values into the function from the calling code, these values are called arguments. When the arguments arrive they are called parameters and are used to initialize real, usable variables: int returnedAnswer = addAToB(10,5);

Also, as we have partly seen in previous examples, we don't have to just use int in our parameters. We can use any C++ type. We can also use as many parameters as necessary to solve our problem, but it is good practice to keep the parameter list as short and therefore manageable as possible.

As we will see in future chapters, we have left a few of the cooler uses of functions out of this introductory tutorial, so that we can learn about related C++ concepts before we take the topic of functions further.

The function body

The body is the part we have been kind of avoiding with comments such as:

// code here 
// some code 

But actually, we know exactly what to do here already! Any C++ code we have learned about so far will work in the body of a function.

Function prototypes

We have seen how to code a function and we have seen how to call one as well. There is one more thing we need to do, however, to make them work. All functions must have a prototype. A prototype is what makes the compiler aware of our function; without a prototype, the entire game will fail to compile. Fortunately, prototypes are straightforward.

We can simply repeat the function's signature, followed by a semicolon. The caveat is that the prototype must appear before any attempt to call or define the function. So the absolutely simplest example of a fully usable function in action is as follows. Look carefully at the comments and where in the code the different parts of the function appear:

// The prototype 
// Notice the semicolon 
int addAToB(int a, int b); 

int main() 
{ 
  
   // Call the function 
   // Store the result in answer 
   int answer = addAToB(2,2); 
  
   // Called before the definition 
   // but that's OK because of the prototype 
  
   // Exit main 
   return 0; 
  
}// End of main 

// The function definition 
int addAToB(int a, int b) 
{ 
    return a + b; 
} 

What the previous code demonstrates is the following:

  • The prototype is before the main function
  • The call to use the function is, as we might expect, inside the main function
  • The definition is after/outside the main function

Note

Note that we can omit the function prototype and go straight to the definition when the definition occurs before the function is used. As our code becomes longer and spreads across multiple files, however, this will almost never happen. We will use separate prototypes and definitions all the time.

Let's see how we can keep our functions organized.

Organizing functions

It's well worth pointing out that, if we have multiple functions, especially if they are fairly long, our .cpp file will quickly become unwieldy. This defeats part of the objective that functions are intended for. The solution that we will see in the next project, is that we can add all of our function prototypes to our very own header file (.hpp or .h). Then we can code all of our functions in another  .cpp file, and then simply add another #include... directive in our main .cpp file. In this way we can use any number of functions without adding any of their code (prototype or definition) to our main code file.

Function gotcha!

Another point that we should discuss about functions is scope. If we declare a variable in a function, either directly or as one of the parameters, then that variable is not usable/visible outside the function. Furthermore, any variables declared outside the function cannot be seen/used inside the function.

The way we should share values between function code and calling code is through the parameters/arguments and the return value.

When a variable is not available, because it is from another function, it is said to be out of scope. When it is available and usable, it is said to be in scope.

Note

Actually, variables declared within any block in C++, are only in scope within that block! This includes loops and if blocks as well. A variable declared at the top of main is in scope anywhere in main. A variable declared in the game loop is only in scope within the game loop, and so on. A variable declared within a function or other block is called a local variable. The more code we write, the more this will make sense. Every time we come across an issue in our code regarding scope, I will discuss it to make things clear. There will be one such issue coming up in the next section. And there are some more C++ staples that blow this issue wide open. They are called references and pointers and we will learn about them in Chapter 7: C++ References, Sprite Sheets, and Vertex Arraysand Chapter 8: Pointers,the Standard Template Library, and Texture Management, respectively.

Final word on functions - for now

There is a lot more we could learn about functions but we know enough about them already to implement the next part of our game. And don't worry if all the technical terms such as parameters, signatures and definitions, and so on have not completely sunk in. The concepts will become clearer when we start to use them.

Absolute final word on functions - for now

It has probably not escaped your attention that we have been calling functions, especially SFML functions, by appending the name of an object, a period before the function name, as in the following example:

spriteBee.setPosition... 
window.draw... 
// etc 

And yet, our entire discussion of functions saw us calling functions without any objects. We can write functions as part of a class or simply as a standalone function. When we write a function as part of a class, we need an object of that class to call the function, and when we have a standalone function we don't.

We will write a standalone function in a minute and we will write classes with functions starting in Chapter 6: Object-Oriented Programming, Classes, and SFML Views. Everything we know so far about functions is relevant in both cases.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.134.108.197