5.3. Comparison with references

Recall from our earlier discussions of pass by reference that the address of operator & allows us to pass by reference:

 1#include <iostream>
 2
 3void by_value(int x) {
 4  x = 99;
 5  std::cout << "in by_value the address of x is "
 6            << &x << '\n';
 7}
 8
 9void by_reference (int& x) {
10  std::cout << "in by_ref the address of x is   "
11            << &x << '\n';
12  x = -1;
13}

In function by_value the statement x = 99; changes the copy provided. The value of x is printed, but is destroyed when x goes out of scope on line 6.

No special character is needed if you want to use a function that takes a reference:

#include <iostream>

int main () {
  int beta = 11;
  std::cout << "the address of beta is "
            << &beta << '\n';

  by_value(beta);

  std::cout << "beta = " << beta << '\n';

  by_reference(beta);

  std::cout << "beta is now "
            << beta << '\n';
}

References do have some definite advantages:

However, there are important things you can’t do with references:

We still need to be able to do all these kinds of memory manipulations. In C++, we achieve these goals using pointers.

5.3.1. Function passing semantics

We can pass pointers to a function that expects a reference:

#include <cassert>

void by_reference (int& x) {
  x = -1;
}

int main() {
  int  i = 5;
  int* p = &i;
  by_reference(p);
  assert (i == -1);
  return 0;
}

If we pass in only p, what happens?

Non-const references vs. pointers

Some programmers consider passing by non-const reference bad style, because the call syntax is the same as pass by value. When a variable is passed into a function by non-const reference there is no visual indication to the programmer of what to expect. Without reading additional documentation or reading the source code, there is no way to know if the function will change its parameter or not.

void func (int& x);

int main() {
  int x = 5;
  func(x);       // will x change?
}

For this reason, a function that takes a non-owning pointer is preferred:

void func (int* x);

int main() {
  int x = 5;
  func(&x);       // Caller expects x to change
}

A function signature is a contract between the function author and the function caller. A function that takes non-const references represents a poorly written contract. Callers don’t know what to expect when the function is called. Even if the parameter isn’t changed today, it might tomorrow. A non-owning pointer makes the intent clear. There is still no requirement to change the parameter, but since the caller is explicitly passing in an address, they can expect it to change.


More to Explore

You have attempted of activities on this page