Department of Engineering

IT Services

Passing values in C++

There are many ways to get values in and out of functions, some of which only pertain to member functions. By careful use of const you can make your code safer, ensuring that variables that shouldn't change are protected from accidental modification.

Simple functions

Here's a simple function which given an integer returns 3 times that integer

call by value

int triple(int number) {
 number=number*3;
 return number;
}

int main() {
i=7;
int j = triple(i)
}

i is "passed by value", meaning that number is initially given the value that i has, but after that there's no connection between the variables. At the end i will be 7 and j will be 21.

call by reference

Sometimes you want to change a variable by passing it to a function. In that situation you need to use "call by reference"

int triple(int& number) { // note the ampersand
 number=number*3;
 return number;
}

int main() {
i=7;
int j = triple(i)
}

Here number is a sort of alias for main's i. After the call to triple, main's i will be 21, and so will j.

Using const

The const keyword can be used in several ways to prevent values being changed. For example, were the previous example's triple changed to the following, the code wouldn't compile, because number can't be changed.

int triple(const int& number) {
 number=number*3;
 return number;
}

We'll see more uses of const later.

Class functions

When calling a routine that's inside a class, all of the above options are available, but const can be used in another way. Suppose triple were inside a class called c. Then a triple routine with the following prototype

int c::triple(const int& number) const;

is like the previous example, but the final const keyword means that in addition the function triple can't change the member variables of c. This safety feature is worth using whenever possible. Here's a complete example -

#include <iostream>
using namespace std;

class c
{
public:
  int value;
  void f(const int& arg) const; 
};
  
// this is a member function of c
void c::f(const int& arg) const {
    cout <<  "arg=" << arg << endl; 
    // The next 2 lines are commented out - they'd cause compilation errors.
    // arg=9;
    // value=9;
}  

int main()
{
int i;
c test_class;
  i=7;
  test_class.f(i);
  cout << "i=" << i << endl; 
}

In C++, references are often used in situations where in the older C language pointers would be used, so if you're already confused by the range of options, it might be best to skip the next section about pointers altogether and go straight onto how to return values.

Argument-passing using pointers

Pointers are best avoided but you'll need to use them sometimes. Here's the earlier references example rewritten to use pointers

int triple(int* number) { 
 *number=*number*3;
 return *number;
}

int main() {
i=7;
int j = foo(&i)
}

This uses "call by reference" (but not using C++'s reference facility!): changing *number will change the i integer in the calling function.

Using const with pointers

In C++, references are always const. With pointers there are extra possibilities (and extra chances for confusion) when using const - the position of const is significant

  • int triple(const int* number); - by reference, but this time the integer in the calling function can't be changed: it's read-only
  • int triple(int* const number); - by reference. The pointer can't be changed, but what it points to (namely the integer of the calling function) can.
  • int triple(const int* const number); - by reference. The pointer can't be changed, neither can what it points to.

Returning values in C++

There are many ways that a function f can return an integer value

  • int f(); - returns an integer by value, as used in the first example. This is easiest and safest.
  • void f(int& answer); - here you give the routine a variable to put the answer into, using call by reference. The advantage of this method is that you can "return" several values. You can use pointers rather than references, but it's less safe.
  • int* f(); - returns a pointer to an integer. Don't do this unless you know what you're doing. There are risks associated with this method, as illustrated here
    #include <iostream>
    using namespace std;
    
    int* f() {
      int j=999;
        cout <<  "j=" << j << endl; 
        // The following line of code returns a pointer to the integer j.
        // Unfortunately j is a local variable whose memory is available
        // for recycling once the function's finished. This recycling 
        // might not happen straight away, but when it does, the 999
        // value could be overwritten. 
        // The line is legal, though some compilers (including the one
        // our students use) give a warning. 
        return &j;
    }  
    
    int main()
    {
    int *ip;
       ip=f();
       cout << "*ip=" << *ip << endl; 
    }
    
  • int& f(); - returns a reference to an integer. This has the same risks as returning a pointer. Note also that a reference (unlike a pointer) can't have a value of 0, so you can't use 0 as an error value

An exercise

(not for the faint-hearted!)

const int* c::f(const int * const arg) const;

is a legal declaration. First work out what it means, then write a program which tests to see if your compiler faithfully obeys each of the uses of const.

See also

"C++ Effective Object-Orientated Software Construction", K. Dattatri, Prentice Hall PTR, 2000 has more details.