Pass by value vs. pass by reference

Example Program #1: Passing by Value

#include<iostream>
using namespace std;

void addOne(int a);

int main() {
    int a = 0;
    addOne(a);
    cout << a << endl;
    return 0;
}

void addOne(int a) {
    a++;
}

// To try:
// 1) change the return type to int and return a,  (don't make any changes to main)
// 2) After doing (1), write a = addOne(a); in main

First, guess what this program will output. Then we'll copy-paste it into Visual Studio and run it. Were you right?

Here's what's going on. There are two variables called a. One is in the scope of main(), and the other is in the scope of the addOne function. When we call addOne(a); from inside main, it creates the variable a inside the addOne scope, and sets the value to zero. But when we run the a++ line inside addOne, it changes the variable in the addOne scope, and makes no changes to the variable in main.

Another way to say the same thing: calling addOne(a) creates a copy of the variable a. The addOne function uses this copy. Changes to the copy to not affect the original a that came from main().

One way to solve this issue is passing by reference!

Example Program #2: Passing by Reference

#include<iostream>
using namespace std;

void addOne(int& a);

int main() {
    int a = 0;
    addOne(a);
    cout << a << endl;
    return 0;
}

void addOne(int& a) {
    a++;
}

// To Try:
// (1) Change the names of the variables.
//       If it is called a inside main, and x inside addOne, they
//        will still be the same variable.

Same thing as before: Guess what this program will output, then copy-paste it into Visual Studio and run it. Were you correct?

Here's my explanation of what's happening here. The '&' inside of "void addOne(int& x)" is what changes the program. It tells the computer to not create any copies, and to use the same variable a in both scopes. So any changes to a made inside the addOne function will be preserved back in main().

Summary

Passing by Value:

Passing by Reference:

Passing by Reference can be more efficient

Like we said above, every time we pass by value, this creates a copy of our variable. When working with large amount of data, the time and storage space spent creating this copy can be really inefficient. In these cases it's important to pass by reference.

Below are two (silly) example programs that give an example of this. In each one, we pass large strings back and forth through functions. You should notice that the by reference code runs MUCH faster.

    stringByValue.cpp
    stringByReference.cpp
  

Passing constant variables

Here's another of those style/code safety things. Sometimes our functions require input parameters to run, but those parameters are not modified in the function. In this case, we label those parameters as const. This is a promise that our function will not modify the parameter in any way. Here are a few silly examples:

    // Uses information from str, but doesn't modify str
    void printStringInfo(const string& str) {
        cout << "Your string is " << str.length() << " characters long.\n";
             << "The first 3 letters are: " << str.substr(0,3) << "\n";
    } // Big question: Why do we use const and & here?

    // Uses the value of x, but doesn't modify x
    double parabolaFunction(const double x) {
        return x*x - 2*x + 1;
    }

    // ERROR: Lists y as a constant, but tries to modify y
    double abs(const double y) {
        if( y > 0)
            return y;
        else {
            y = -1*y;
            return y;
        }
    }
  

Here's one example that affects your life, where labeling things as const is actually helpful.

    int exampleFunction(const int x) {
        if( x = 3 ) {   // Error: = instead of ==. But Visual Studio will catch this one!
            // do something here
        }
        // whatever other code
    }
  

I read a funny quote on the internet: "When writing industrial-strength code, you should always assume that your coworkers are psychopaths trying to get you any way they can (especially since it's often yourself in the future)." From experience, this is true (and I'm the only one working on my project!)

Practice Problems

Problem #1

What is the output of the following?

#include <iostream>
using namespace std;

void figureMeOut(int& x, int y, int& z);

int main()
{
    int a, b, c;
    a = 10;
    b = 20;
    c = 30;
    figureMeOut(a,b,c);
    cout << a << " " << b << " " << c << endl;
}

void figureMeOut(int& x, int y, int& z)
{
    cout << x << " " << y << " " << z << endl;
    x = 1;
    y = 2;
    z = 3;
    cout << x << " " << y << " " << z << endl; 
}

Problem #2

Write a void function called zeroBoth that has two call-by-reference inputs, both of which are variables of type int, and sets the values of both variables to 0.

#include <iostream>
using namespace std;

// Declare the function zeroBoth here.

int main()
{
    int a = 1, b = 2;
    zeroBoth(a, b);
    cout << a << " " << b << endl;  // should cout 0 0
}

// Define the function zeroBoth here.

Problem #3

Write a void function definition for a function called addTax. The function addTax has two formal inputs:

The function should change the value of cost so that it includes sales tax.

#include <iostream>
using namespace std;

// Declare addTax here

int main()
{
    double price = 10;
    const double tax = 8.75;
    addTax(tax, price);  // should change price to include tax.
    cout << price << " " << tax << endl; 
}

// Define addTax here

Problem #4

Write a void function that takes two int variables and sorts them. That is: It switches their values if the value of the second is smaller than the value of the first, otherwise it does nothing.

#include <iostream>
using namespace std;

// Declare sort2 here

int main()
{
    int a = 2, b = 1;
    sort2(a, b);
    cout << a << " " << b << endl;  // For this particular example should get a=1, b=2
}

// Define sort2 here

Problem #5

Write a void function that takes three int variables and sorts them. Do this in a way that uses your sort2 function from problem #4.

#include <iostream>
using namespace std;

// Declare sort3 here.

int main()
{
    int a = 2, b = 3, c = 1;
    sort3(a, b, c);
    cout << a << " " << b << " " << c << endl;  // should output 1 2 3
}

// Define sort3 here. Make sure to use your sort function from problem #4.

An intro to sorting algorithms (if we have time)

In this problem we're going to input a string from the user, and rearrange the letters so that they're in alphabetical order. There are a lot of ways to do this. The one we're going to go over today is called "bubble sort". Here is the idea of the program: first we find the letter that should go first, then put it in the correct position. Once that's been done, we can ignore that letter for the rest of the program and focus on the rest. Repeat this process, and eventually all the letters will be in order. Here is the code:

#include<iostream>
#include<string>
using namespace std;

void swap(char& a, char& b);

int main() {

    cout << "Please input a string:\n";
    string x;
    getline(cin,x);

    for(int i=0; i < x.length(); i++) {
        for(int j=i+1; j < x.length(); j++) {
            if(x.at(i) > x.at(j))
                swap(x.at(i),x.at(j));
        }
    }

    cout << "I rearranged your string and got:\n" << x << endl;
    return 0;
}

void swap(char& a, char& b) {
    char temp = a;
    a = b;
    b = temp;
}