Mark As Completed Discussion

In C++, functions are fundamental building blocks for structuring code. They promote code reuse and make complex programs easier to manage, understand, and debug.

You can think of a function much like a web development API endpoint - it's a url that performs a specific task and gives a response when called, only in C++ it's a block of code that performs a particular task and gives a response when called. Imagine designing the algorithm that defines how a financial app's "Transfer" button works, that's exactly how functions work.

A function in C++ has the following structure:

  • The return type: This indicates what type of data the function will return--for example, int for an integer, double for a double precision floating point, or void if the function does not return a value.
  • The function name: This is the identifier by which the function can be called.
  • The parameters (also called arguments): These are inputs to the function enclosed in parentheses and separated by commas. Parameters are optional; that is, a function may not contain any parameters.
  • The function body: This is enclosed in curly braces and contains a collection of statements that define what the function does.

Below is an example of a simple function that adds two integers together and returns the result:

SNIPPET
1int add(int a, int b) {
2  return a + b;
3}

This add function can then be called elsewhere in your code. For instance, to add 10 and 5, you could write: int sum = add(10,5);. After this line, sum would contain the value 15.

CPP
OUTPUT
:001 > Cmd/Ctrl-Enter to run, Cmd/Ctrl-/ to comment

Try this exercise. Click the correct answer from the options.

What is the correct keyword to define a function in C++ that does not return a value?

Click the option that best answers the question.

  • none
  • void
  • empty
  • return 0

In programming and particularly in web development, function parameters could be thought of analogously as the 'query parameters' of a URL for an HTTP GET request. They provide an interface to pass information to your function. This makes your function more flexible and reusable. That said, C++ goes further by allowing pass by value and pass by reference.

When passing parameter by value, a copy of the actual argument's value is made into the function's parameter. Modifying the parameter inside this function does not impact anything outside the function. Zero side-effects. This is commonly used when the parameter is of basic data type. To illustrate, below is example code which shows a transfer between two banking accounts using pass-by-value:

TEXT/X-C++SRC
1#include <iostream>
2using namespace std;
3
4void transfer(int from_account, int to_account, double amount) {
5  from_account -= amount;
6  to_account += amount;
7}
8
9int main() {
10  int account1 = 5000;
11  int account2 = 2000;
12  transfer(account1, account2, 1000);
13  cout << "Account 1: " << account1 << endl;
14  cout << "Account 2: " << account2 << endl;  // Note the account balances remain unchanged
15}

The code shows that pass-by-value will not change the original variables account1 and account2.

Passing by reference, on the other hand, means the actual argument is passed to the function. Changes to the parameter inside the function reflect in the actual argument. It’s as if the original variable (from the calling function’s scope) is used in the called function. This is useful for objects, complex data structures, and when you actually want the called function to modify the caller’s original variables:

TEXT/X-C++SRC
1#include <iostream>
2using namespace std;
3
4void transfer(int& from_account, int& to_account, double amount) {
5  from_account -= amount;
6  to_account += amount;
7}
8
9int main() {
10  int account1 = 5000;
11  int account2 = 2000;
12  transfer(account1, account2, 1000);
13  cout << "Account 1: " << account1 << endl;  // The account balances are updated now
14  cout << "Account 2: " << account2 << endl;
15}

As seen above, pass-by-reference alters the original variables account1 and account2.

CPP
OUTPUT
:001 > Cmd/Ctrl-Enter to run, Cmd/Ctrl-/ to comment
CPP
OUTPUT
:001 > Cmd/Ctrl-Enter to run, Cmd/Ctrl-/ to comment

Build your intuition. Click the correct answer from the options.

What will happen to the original values of variables account1 and account2 from the following code snippet?

TEXT/X-C++SRC
1#include <iostream>
2using namespace std;
3
4void transfer(int from_account, int to_account, double amount) {
5  from_account -= amount;
6  to_account += amount;
7}
8
9int main() {
10  int account1 = 5000;
11  int account2 = 2000;
12  transfer(account1, account2, 1000);
13  cout << "Account 1: " << account1 << endl;
14  cout << "Account 2: " << account2 << endl;
15}

Click the option that best answers the question.

  • The values of `account1` and `account2` will decrease and increase respectively.
  • The values of `account1` and `account2` remain unchanged.
  • The values of `account1` and `account2` will both increase.
  • The values of `account1` and `account2` will both decrease.

Understanding Recursion in C++

In many ways, we can think of recursion in programming similar to recursion in mathematics or fractals in art, where a pattern repeats within itself. But in programming, recursion refers to a function that calls itself within its definition. It's a powerful tool, allowing us to write elegant solutions to problems that can be naturally split into smaller, similar subproblems.

Consider the Fibonacci sequence, a classic example of recursion. Each number in the sequence is the sum of the two preceding ones. We can express this mathematically as follows:

  • fibonacci(0) = 0
  • fibonacci(1) = 1
  • fibonacci(n) = fibonacci(n-1) + fibonacci(n-2) for n > 1

This translates directly into a recursive function in C++, as shown in the accompanying example code. For the base cases, fibonacci(0) and fibonacci(1), we return the input n directly. For larger numbers, we make two recursive calls to calculate the two preceding fibonacci terms, and return their sum.

This simple function doesn't just calculate Fibonacci numbers - it also demonstrates a deeper property of recursion. Each recursive call to fibonacci makes two more calls, creating a tree of calculations that grows exponentially with input size. Understanding this property is crucial to understanding recursion, and to understanding why recursion can be both powerful and tricky to use effectively.

CPP
OUTPUT
:001 > Cmd/Ctrl-Enter to run, Cmd/Ctrl-/ to comment

Are you sure you're getting this? Is this statement true or false?

Recursive function calls in C++ do not create a tree of calculations that grows exponentially with input size.

Press true if you believe the statement is correct, or false otherwise.

Now that we've understood the concept of recursion in C++, let's see how to implement it. A classic problem in programming used to illustrate recursion is the calculation of the factorial of a number.

In mathematics, the factorial of a non-negative integer n is the product of all positive integers less than or equal to n. It is denoted by n!. For example, 5! is 5*4*3*2*1 = 120.

The factorial problem can be divided into two sub-problems:

  • Base Case: If n = 1, the function returns 1, because 1! equals to 1.
  • Recursive Case: If n > 1, then the function calls itself with n - 1, multiplying n with the factorial of n-1.

Factorials are used in many areas of computer science, including algorithm complexity computation (Big O notation), artificial intelligence, and financial calculations. In the attached code we're exploring how to compute a factorial of a number (in this case 5) using recursion in C++.

In the code, the factorial() function is defined such that it calls itself (recursion) to compute a factorial of a given number. The function takes one argument, n, and uses the if statement to check if n equals 1 which is our base case. If n > 1, then the function calls itself with the argument n-1; and n is multiplied with the result. This operation is performed until n equals 1, and that's where the function stops calling itself (the base case). The factorial of n is then returned to the calling function and printed to the console.

CPP
OUTPUT
:001 > Cmd/Ctrl-Enter to run, Cmd/Ctrl-/ to comment

Build your intuition. Click the correct answer from the options.

In the context of implementing a recursive function in C++ to calculate the factorial of a number, what is the base case?

Click the option that best answers the question.

  • If `n = 2`, the function returns `2`, because `2!` equals to `2`.
  • If `n = 0`, the function returns `1`, because `0!` equals to `1`.
  • If `n = 1`, the function returns `1`, because `1!` equals to `1`.
  • If `n > 1`, then the function calls itself with `n - 1`.

In the topic of recursion, an important concept that is often brought up is tail recursion. A special kind of recursion, where the function's recursive call is the last operation it performs. It's termed as 'tail' because there's nothing to do after the function calls itself - the call to the recursive function is literally at the 'tail end' of the function.

One of the primary benefits of tail recursion is its efficient utilization of memory. In simple recursion, for every recursive call, the system must 'remember' previous calculations leading up to that call. This can consume considerable memory and lead to a stack overflow for in-depth recursion chains. However, in tail recursion, the calculations are done upfront, with the result passed as an argument to the recursive call. This allows the system to discard old stack frames as new ones are created, effectively performing a 'swap' operation instead of a 'push', significantly reducing the risk of a stack overflow and providing a more efficient execution flow.

Algorithms that are implemented with tail recursion can be directly mapped to iterations, and hence, are preferred in programming, especially in AI and finance-related calculations.

Let's take the example of a recursive function for calculating the factorial of a number. The tail-recursive version of this function would be as below:

CPP
OUTPUT
:001 > Cmd/Ctrl-Enter to run, Cmd/Ctrl-/ to comment

Build your intuition. Fill in the missing part by typing it in.

In tail recursion, the calculations are done upfront, with the result passed as an argument to the recursive call. This allows the system to discard old stack frames as new ones are created, effectively performing a '_' operation instead of a 'push', significantly reducing the risk of a stack overflow.

Write the missing line below.

As we wrap up our discussion on functions and recursion in C++, let's revisit our factorial function, now implemented using tail recursion. This form of recursion, as we learned, is preferred in scenarios where efficiency is paramount, such as in web-based applications or AI computations.

In the context of a senior engineer working on financially-related programs, understanding the potential stack overflow issues of traditional recursion and how tail recursion mitigates these risks can be immensely beneficial. For instance, programs that deal with large amounts of data or require complex computations can greatly benefit from the memory efficiency of tail recursive function calls.

Consider the C++ code below for calculating factorials using tail recursion. In this case, we've added a second parameter to the function - result - that accumulates the factorial result. Notice that the recursive call is the very last operation that the function performs, ensuring we have a tail recursive function.

This updated factorial function—alongside everything else we've covered—represents some of the key elements of functions and recursion in C++, which, as you now know, play a crucial role in the robustness, efficiency, and versatility of C++ programming.

CPP
OUTPUT
:001 > Cmd/Ctrl-Enter to run, Cmd/Ctrl-/ to comment

Are you sure you're getting this? Is this statement true or false?

In a tail recursive function, the recursive call does not have to be the very last operation the function performs.

Press true if you believe the statement is correct, or false otherwise.

Generating complete for this lesson!