News & UpdatesProgrammingWeb programming StoreMy Projects
Links
Affiliates

C++ Tutorial – 12 – Functions

Functions are reusable code blocks that will only execute when called.

Defining functions

A function can be created by typing void followed by the function’s name, a set of parentheses and a code block. The void keyword means that the function will not return a value. The naming convention for functions is the same as for variables – a descriptive name with each word initially capitalized, except for the first one.

void myFunction() 
{
  cout << "Hello World";
}

Calling functions

The function above will simply print out a text message when it is called. To invoke it from the main function the function’s name is specified followed by a set of parentheses.

int main()
{
  myFunction(); // "Hello World"
}

Function parameters

The parentheses that follow the function name are used to pass arguments to the function. To do this the corresponding parameters must first be added to the function declaration in the form of a comma separated list.

void myFunction(string a, string b)
{
  cout << a + " " + b;
}

A function can be defined to take any number of arguments, and they can have any data types. Just ensure the function is called with the same types and number of arguments.

myFunction("Hello", "World"); // "Hello World"

To be precise, parameters appear in function definitions, while arguments appear in function calls. However, the two terms are sometimes used interchangeably.

Default parameter values

It is possible to specify default values for parameters by assigning them a value inside the parameter list.

void myFunction(string a, string b = "Earth")
{
  cout << a + " " + b;
}

Then, if that argument is unspecified when the function is called the default value will be used instead. For this to work it is important that the parameters with default values are to the right of those without default values.

myFunction("Hello"); // "Hello Earth"

Function overloading

A function in C++ can be defined multiple times with different arguments. This is a powerful feature called function overloading that allows a function to handle a variety of parameters without the programmer using the function needing to be aware of it.

void myFunction(string a, string b) { cout << a+" "+b; }
void myFunction(string a)           { cout << a; }
void myFunction(int a)              { cout << a; }

Return statement

A function can return a value. The void keyword is then replaced with the data type that the function will return, and the return keyword is added to the function’s body followed by an argument of the specified return type.

int getSum(int a, int b)
{
  return a + b;
}

Return is a jump statement that causes the function to exit and return the specified value to the place where the function was called. For example, the function above can be passed as an argument to the output stream since the function evaluates to an integer.

cout << getSum(5, 10); // "15"

The return statement can also be used in void functions to exit before the end block is reached.

void dummy() { return; }

Note that although the main function is set to return an integer type, it does not have to explicitly return a value. This is because the compiler will automatically add a return zero statement to the end of the main function.

int main() { return 0; }

Forward declaration

An important thing to keep in mind in C++ is that functions must be declared before they can be called. This does not mean that the function has to be implemented before it is called. It only means that the function’s header needs to be specified at the beginning of the source file, so that the compiler knows that the function exists. This kind of forward declaration is known as a prototype.

void myFunction(int a); // prototype
int main()
{
  myFunction(0);
}
void myFunction(int a) {}

The parameter names in the prototype do not need to be included. Only the data types must be specified.

void myFunction(int);

Pass by value

In C++, variables of both primitive and object data types are by default passed by value. This means that only a copy of the value or object is passed to the function. Therefore, changing the parameter in any way will not affect the original, and passing a large object will be very slow.

#include <iostream>
#include <string>
using namespace std;
 
void set(int i)    { i = 10; } 
void set(string s) { s = "Hello World"; }
 
int main()
{
  int x = 0;     // value type
  set(x);        // value copy is passed
  cout << x;     // "0"
 
  string y = ""; // reference type
  set(y);        // object copy is passed
  cout << y;     // ""
}

Pass by reference

Alternatively, to instead pass a variable by reference you just need to add an ampersand before the parameter’s name in the function’s definition. When passing arguments by reference, both primitive and object data types can be changed or replaced and the changes will affect the original.

void set(int& i) { i = 10; }
 
int main()
{
  int x = 0; // value type
  set(x);    // reference is passed
  cout << x; // "10"
}

Pass by address

As an alternative to passing by reference, arguments may also be passed by address using pointers. This passing technique serves the same purpose as passing by reference, but uses pointer syntax instead.

void set(int* i) { *i = 10; }
 
int main()
{
  int x = 0;  // value type
  set(&x);    // address is passed
  cout << x;  // "10"
}

Return by value, reference or address

In addition to passing variables by value, reference or address, a variable may also be returned in one of these ways. Most commonly, a function returns by value, in which case a copy of the value is returned to the caller.

int byVal(int i) { return i + 1; }  
 
int main()
{
  int a = 10;
  cout << byVal(a); // "11"
}

To return by reference instead, an ampersand is placed after the function’s return type. The function must then return a variable and may not return an expression or literal, as can be done when using return by value. The variable returned should never be a local variable, since the memory to these variables is released when the function ends. Instead, return by reference is commonly used to return an argument that has also been passed to the function by reference.

int& byRef(int& i) { return i; } 
 
int main()
{
  int a = 10;
  cout << byRef(a); // "10"
}

To return by address the dereference operator is appended to the function’s return type. This return technique has the same two restrictions as when returning by reference – the address of a variable must be returned and that returned variable must not be local to the function.

int* byAdr(int* i) { return i; }
 
int main()
{
  int a = 10;
  cout << *byAdr(&a); // "10"
}

Inline functions

A thing to keep in mind when using functions is that every time a function is called, a performance overhead occurs. To potentially remove this overhead you can recommend that the compiler inlines the calls to a specific function by using the inline function modifier. This keyword is best suited for small functions that are called inside loops. It should not be used on larger functions since inlining these can severely increase the size of the code, which will instead decrease performance.

inline int myInc(int i) { return i++; }

Note that the inline keyword is only a recommendation. The compiler may in its attempts to optimize the code choose to ignore this recommendation and it may also inline functions that do not have the inline modifier.

Recommended additional reading:
Sams - Teach Yourself C++ in One Hour a Day