Call by value and call by reference

Call by value and call by reference.

Call by value.

Example: The function countdown(n) prints the numbers from n down to 0.

#include <iostream>
using namespace std;

void countdown(int);

int main() {
  int start = 5;
  countdown(start);
  // what is the value of 'start' at this point?
}

void countdown(int n) {
  while(n>=0) {
    cout << n << endl;
    n--;
  }
}
5
4
3
2
1
0

What is the value of the variable start in the main function after the function call countdown(start)? Is it still equal to 5 or equal to 0?

When you call the function countdown(start), the formal parameter of the function (n) is created as a brand new variable, a chunk of memory is allocated for it, and the value of the argument (in this case, the number 5) is copied to it.

So, when you call the function, it gets its own independent copy of all the arguments passed to it.

Thus any change to the parameter (for example, the statement n--; in this case), would affect only the local copy of the passed value, not the original value of the variable start.

Therefore, after the function call, the value of the variable start remains unchanged and equal to 5.


This approach, when you pass the argument by value is called Call by value. And it is a really great idea! Because a function is an independent module of your program, and it sould not inadvetantly affect the variables declared somewhere else in the program. The only result of the function sould be just the value it returns.

In the perfect world, this would be enough.

However, why Call by value is not always what we want?

void get_coords(int location, int &x, int &y) {
  x = location % 10;
  y = location / 10;
}
int object_dimensions(huge_object &obj) {
  return obj.width * obj.height;
}

Call by reference

Example: Function make_even(x) increments its argument x if it is odd. The argument is passed by reference.

#include <iostream>
using namespace std;

void make_even(int &x);

int main() {
  int x = 5;
  make_even(x);
  cout << "x = " << x << endl;
}

void make_even(int &x) {
  if (x % 2 == 1) 
    x = x + 1;
}

x = 6

Example: Function normalize(x,y) normalizes the vector (x,y) by dividing it by its Euclidean norm sqrt(x2 + y2). The function updates its arguments x and y in place (they are passed by reference).

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

// normalize components of the vector (x,y)
bool normalize(double &x, double &y);

int main() {
  double x = 1.5, y = -0.7; 
  normalize(x, y);
  cout << "x = " << x << endl << "y = " << y << endl;
}

bool normalize(double &x, double &y) {
  double len = sqrt(x*x + y*y);
  if (len > 0.0) {
    x = x / len;
    y = y / len;
    return true;
  }
  else
    return false;
}


x = 0.906183
y = -0.422885

swap(a,b)

#include <iostream>
using namespace std;

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

int main() {
  int x = 5, y = 10;
  cout << "x = " << x << ", y = " << y << endl;
  swap(x, y);
  cout << "x = " << x << ", y = " << y << endl;
  swap(x, y);
  cout << "x = " << x << ", y = " << y << endl;
}

void swap(int &a, int &b) {
  int tmp = a;
  a = b;
  b = tmp;
}

x = 5, y = 10
x = 10, y = 5
x = 5, y = 10

Exercise

Write a function

void rational(double x, int &n, int &d);

which for the given positive double x, searches for the numerator n and denominator d that are positive integers <= 10000, such that the error |x - n/d| is the smallest possible.

const call by reference

With a constant call by reference, we can avoid copying the argument, at the same time not allowing to make any changes to this argument.

int compute(const int &arg);

Overloading a function name

Multiple functions can be defined using the same name, however they must differ either

Notice that the difference in the type of the value returned is not enough to allow an overloaded definition.

The overloaded function definitions must differ in their formal parameters.

#include <iostream>
using namespace std;

double average(double x1, double x2);
double average(double x1, double x2, double x3);

int main() {
  cout << average(5, 10) << endl;
  cout << average(12, 13, 15) << endl;
}

double average(double x1, double x2) {
  return 0.5 * (x1 + x2);
}

double average(double x1, double x2, double x3) {
  return (x1 + x2 + x3) / 3.0;
}
7.5
13.3333

Default parameters

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

// vector length (Euclidean norm) for 1D, 2D, and 3D
double length(double x, double y = 0.0, double z = 0.0);

int main() {
  cout << length(4,3,5) << endl;
  cout << length(7,4) << endl;
  cout << length(10) << endl;
}

double length(double x, double y, double z) {
  return sqrt(x*x + y*y + z*z);
}

7.07107
8.06226
10