C++ Functions

A function is a block of statements that perform a specific task, which can be used multiple times within a program. In C++, as with a lot of languages, there are a number of functions built in, which are part of the C++ Standard Library, however, it is also possible to create user defined functions. The statements within a function are only executed once the function is called, so, if the function is never called then they won’t run. In C++, a function can be defined and called as follows.

// Function definition.
return_type function_name(parameter_type parameter_name)
{
    // Statement(s) to execute.
}

// Function call.
function_name(parameters);

A function can return an item of data of a particular type. When a function is defined, the return type is specified prior to the function name, which is used to call it. If no value is to be returned from a function, then the keyword ‘void’ is used. The rules for naming a function are similar to a variable, it must start with a letter or underscore, but can then be followed by either letters, numbers or underscores. Any parameters, or arguments, that are to be passed to the function are enclosed in parenthesis after the name, separated by commas, if there is more than one. The parameter type is the type of data that the parameter holds, such as ‘char’, ‘string’, ‘int’ ‘float’ and ‘double’. If there are no parameters to be passed, then the parenthesis must still be included, but with nothing enclosed.

To call a function, it is simply a case of specifying the function name, along with any parameters in the parenthesis after the name. Again, if there are no parameters, the parenthesis must still be included.

One stipulation with functions is that they must be defined before they can be used. The function call cannot be above the function definition. To solve this issue, function prototypes can be used. Function prototypes are placed above the ‘main’ function.

return_type function_name(parameter_type parameter_name);

A function prototype should match the first line of the function definition, except there must be a semi-colon at the end. Once this is done it doesn’t matter what order the function call and definition are.

Below is a simple example of a function prototype, call and definition. The function, called ‘addition’, takes two parameters of type ‘int’ and returns an ‘int’ after adding the two parameters together. The function contains one statement, a ‘return’ statement, where the addition is done directly within it. The function call is made as part of a ‘cout’ statement.

#include <iostream>

using namespace std;

// Function prototype.
int addition(int num1, int num2);

int main()
{
    
    // Numbers to add together.
    int num1 = 10;
    int num2 = 15;
    
    // Function call as part of 'cout' statement.
    cout << "num1 and num2 added together equals: " << addition(num1, num2) << endl;
    
}

// Function definition.
int addition(int num1, int num2)
{
    // Return result.
    return num1 + num2;
}

The output from the above is shown in the console.

num1 and num2 added together equals: 25

As mentioned previously, a function doesn’t always need to return a value. In such circumstances, the ‘void’ keyword is used instead of the return type. Below is an example of a function that takes no parameters and does not return a value. All it does is output the message, ‘Hello World!’ to the console, using a ‘cout’ statement.

#include <iostream>

using namespace std;

// Function prototype.
void hello();

int main()
{
    
    // Function call.
    hello();
    
}

// Function definition.
void hello()
{
    cout << "Hello World!" << endl;
}

The above example can be enhanced to include parameters, such as a person’s name. Here, two string parameters for first name and last name are passed to the function, separated by commas and used within the greeting displayed.

#include <iostream>

using namespace std;

// Function prototype.
void hello(string fname, string lname);

int main()
{
    
    // Name variables.
    string fname = "Fred";
    string lname = "Bloggs";
    
    // Function call.
    hello(fname, lname);
    
}

// Function definition.
void hello(string fname, string lname)
{
    cout << "Hello " << fname << " " << lname << "!" << endl;
}

The resulting output from this is as follows.

Hello Fred Bloggs!

It should be noted that passing variables as parameters in this way is known as pass by value because a copy of the variable value is being passed and not the variable itself. If the values of ‘fname’ and ‘lname’ were changed within the ‘hello’ function, these changes would not be available back in the ‘main’ function.

Passing By Reference

It is possible to pass a variable by reference by using a reference. This would mean that the updated value would be accessible back in the ‘main’ function.

Expanding on the previous example, the ‘hello’ function now takes three parameters, all of which are references to strings, denoted by the ampersand before each parameter name. The first two are the first name and last name, as before and the third is the greeting, which will be updated within the ‘hello’ function. The function now just constructs the message within the greeting variable, with the displaying of it carried out back in the ‘main’ function.

#include <iostream>

using namespace std;

// Function prototype.
void hello(string &fname, string &lname, string &greeting);

int main()
{
    
    // Name variables.
    string fname = "Fred";
    string lname = "Bloggs";
    
    // Greeting.
    string greeting = "Hello ";
    
    // Function call.
    hello(fname, lname, greeting);
    
    // Display the greeting.
    cout << greeting << endl;
    
}

// Function definition.
void hello(string &fname, string &lname, string &greeting)
{
    // Construct greeting.
    greeting += fname;
    greeting += " ";
    greeting += lname;
    greeting += "!";
}

The resulting output is the same as in the previous example.

Hello Fred Bloggs!

Constant Parameters

To ensure that the name parameters are not changed within the ‘hello’ function, one further enhancement can be made using the ‘const’ keyword. The ‘const’ keyword can be placed before the parameter type, where necessary, in both the function prototype and definition, to turn them in to constant parameters, meaning that they cannot be altered within the function.

#include <iostream>

using namespace std;

// Function prototype.
void hello(const string &fname, const string &lname, string &greeting);

int main()
{
    
    // Name variables.
    string fname = "Fred";
    string lname = "Bloggs";
    
    // Greeting.
    string greeting = "Hello ";
    
    // Function call.
    hello(fname, lname, greeting);
    
    // Display the greeting.
    cout << greeting << endl;
    
}

// Function definition.
void hello(const string &fname, const string &lname, string &greeting)
{
    // Construct greeting.
    greeting += fname;
    greeting += " ";
    greeting += lname;
    greeting += "!";
}

Overloading Functions

Overloading functions refers to the ability to have more than one function with the same name, but a differing list of parameters. The parameter list can differ either by the types of parameters being passed, such as ‘int’ or ‘float’, as well as the number of parameters.

The example below contains two functions called ‘hello’, the first of which has one parameter for the greeting, whilst the latter has three, two for the name, as well as one for the greeting. An ‘if’ statement is used to determine if the name variables are set in the ‘main’ function and call the appropriate ‘hello’ function, depending on whether they are or not.

#include <iostream>

using namespace std;

// Function prototypes.
void hello(string &greeting);
void hello(const string &fname, const string &lname, string &greeting);

int main()
{
    
    // Name variables.
    string fname = "Fred";
    string lname = "Bloggs";
    
    // Greeting.
    string greeting = "Hello ";
    
    // Check if there is a name.
    if (fname != "" && lname != "")
    {
        // Function call.
        hello(fname, lname, greeting);
    }
    else
    {
        // Function call.
        hello(greeting);
    }
    
    // Display the greeting.
    cout << greeting << endl;
    
}

// Function definition.
void hello(string &greeting)
{
    // Construct greeting.
    greeting = "Hello World!";
}

// Function definition.
void hello(const string &fname, const string &lname, string &greeting)
{
    // Construct greeting.
    greeting += fname;
    greeting += " ";
    greeting += lname;
    greeting += "!";
}

If the name variables aren’t set, then the version of the function with one parameter is called, setting the greeting to ‘Hello World!’. If the name variables are set, which in this case they are, then the version of the function with three parameters is called and the output is the same as before.

Variable Scope

Variables in C++ can be defined anywhere within a program. The scope of a variable refers to the area of a program that it can be used. Variable scope in C++ can either be local or global.

Variables that have a global scope are defined outside of a function and are available to the whole program, whereas, variables with a local scope are defined within a function and are only accessible within the function they are defined. Variables can also be local to a loop, or another block that is enclosed in curly braces, inside a function.

The example below is a variation on the one above. Instead of the greeting variable being defined in the ‘main’ function and passed as a reference to the ‘hello’ function, it is defined as a global variable outside of any function. This variable is accessible to both the ‘main’ and ‘hello’ functions.

#include <iostream>

using namespace std;

// Function prototypes.
void hello();
void hello(const string &fname, const string &lname);

// Global greeting variable.
string greeting = "Hello ";

int main()
{
    
    // Name variables.
    string fname = "Fred";
    string lname = "Bloggs";
    
    // Check if there is a name.
    if (fname != "" && lname != "")
    {
        // Function call.
        hello(fname, lname);
    }
    else
    {
        // Function call.
        hello();
    }
    
    // Display the greeting.
    cout << greeting << endl;
    
}

// Function definition.
void hello()
{
    // Construct greeting.
    greeting = "Hello World!";
}

// Function definition.
void hello(const string &fname, const string &lname)
{
    // Construct greeting.
    greeting += fname;
    greeting += " ";
    greeting += lname;
    greeting += "!";
}

As before, the greeting is displayed in the console as follows.

Hello Fred Bloggs!

It should be noted that, passing a variable by reference is considered better practice than using a global variable, in most instances, because it is easier to keep track of what functions can alter it, however, there may be circumstances where using a global variable is desirable.