next up previous contents
Next: Classes Up: Getting Started in C++ Previous: Structs   Contents

Subsections


References

A reference is a special object which refers to another, ``normal'', object. In other words it is an object which has its own name and type, but ``uses'' the data of another object (which must be of the same type, of course). It is similar to the links to files which certain OSs let you create: they have a name of their own, but use the data of some other file. A reference is declared like this:

int i;      //i is a normal int
int& r = i; //r is a reference to i
(Notice the & after the type name.)

You must initialize a reference when you declare it, because otherwise it wouldn't have an object to ``use''.

In their simplest form, references are just a new name for an already existing object, like here:

#include <iostream>
using namespace std;

int main() {
    int i = 5;
    int& r = i; // Declare a reference to i
    cout << i << "\n";
    cout << r << "\n";

    // Now modify i
    i = 7;
    cout << i << "\n";
    cout << r << "\n";

    // Now modify i through r
    r = 9;
    cout << i << "\n";
    cout << r << "\n";
}
The output of this program is
5
5
7
7
9
9


Pass-by-Reference

Of course, used that way references are perfectly useless. In fact, that's not how they are generally used. As a reference is not just an alias for some existing object, but object of its own, which refers to another object, it can be used in many other ways. As you can modify the original object through a reference, it is possible to pass a reference to some object and not the object itself to a function, which is thus able to modify the original object, just like here:

#include <iostream>
using namespace std;

void increment(int& i) {
    ++i; // Remember that ++i is a short form for i = i + 1
}

int main() {
    int test = 10;
    cout << test << "\n";
    increment(test);
    cout << test << "\n";
}
The output of this program is
10
11

This method to pass arguments is called passing by reference, opposed to passing by value as described in 6.7.

Of course, you can not pass a constant value by reference; in other words, something like

increment(13);
is illegal because it is of course not possible to increment 13. You can only pass variables like this.

Passing by reference is especially useful for structs; it is the simplest (but not only) way to overcome the limitation explained in 7.1.2. For example, now we can write a function to read in a Wizard object from standard input:

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

struct Wizard {
    string name;
    int age;
};

// Notice how we use references to be able to modify
// the wizard inside the function
void wizard_input(Wizard& wizard) {
    cout << "Name: ";
    cin >> wizard.name;
    cout << "Age: ";
    cin >> wizard.age;
}

// Here we pass by value and not by reference because
// we don't need to modify anything
void wizard_output(Wizard wizard) {
    cout << wizard.name << " is " << wizard.age << ".\n";
}

int main() {
    Wizard wizard;
    cout << "Please tell me about your wizard:\n";
    wizard_input(wizard);
    wizard_output(wizard);
}
This program works like this:
Please tell me about your wizard:
Name: Gandalf
Age: 123
Gandalf is 123.


Speed

If you don't want to read this now, or don't understand it, feel free skipping it. You should only know that it is useful to pass structs by reference even if you don't want to modify the object you're passing, because it is generally faster.

Passing by reference is not only useful to pass arguments to functions which need to modify them; it may also be faster and require less memory than passing by value.

A reference generally has the size of a machine word; this means that on a 32-bit processor (like all Pentiums, from I to IV, for example) it is 32 bits wide, on a 64 bit processor 64 bits wide and so on. Then, arguments passed by reference need to be dereferenced by the compiler while arguments passed by value don't; this makes references slower. But if you pass a reference the compiler does not need to copy the argument, which is, of course, and advantage.

As a rule of thumb you can say that a POD (explanation follows) is faster if passed by value, while a complex type like a struct -- which is generally also quite a lot bigger than a POD -- is faster if passed by reference. A POD is a so-called built-in type, like int and double. Notice, though, that string -- and many others -- are part of C++, but not PODs anyway. As another rule of thumb (yes, I love them) you can say that the types you can use even without including any headers are PODs, while the others aren't. This isn't always true, though, and that's why it is a rule of thumb.

Constants

In some cases you want to pass an argument by reference even if you don't want to modify it -- be it for the sake of speed (8.1.1), memory (8.1.1, too) or some other reason. It would be nice if you could tell the compiler not to allow that object to be modified. That's what the const keyword is here for. But before introducing how to use it on references, let's use it on plain objects.

Constant Objects

Sometimes it is necessary to use the same constant -- for example the number 20 -- in several places in a program, as here:

#include <iostream>
using namespace std;

int main() {
    cout << "Countdown will last 20 minutes.\n";
    for (int i = 20; i > 0; --i)
        cout << i << " ";

    cout << "GO!\n";
}
This is of course very ugly. If you wanted to modify the program to use 10 instead of 20, you'd have to change it in more than one place. This is not only tedious, but also dangerous. You might forget one, and make the program buggy. It might seem a good idea to replace the constant with a variable, and to initialize the variable at the very beginning, like this:
#include <iostream>
using namespace std;

int main() {
    int count = 20;

    cout << "Countdown will last " << count << " minutes.\n";
    for (int i = count; i > 0; --i)
        cout << i << " ";

    cout << "GO!\n";
}
This is of course already a lot better. There is still a problem, though. You might modify the variable by mistake, like here:
#include <iostream>
using namespace std;

int main() {
    int count = 20;

    cout << "Countdown will last " << count << " minutes.\n";
    count = 13; // OOPS!
    for (int i = count; i > 0; --i)
        cout << i << " ";

    cout << "GO!\n";
}
Of course, the program wouldn't work correctly anymore.

What we need is a variable which cannot be modified. That's exactly what a constant is. The main differences between variables and constants are:

Using constants, our nice countdown program looks like this:

#include <iostream>
using namespace std;

int main() {
    const int count = 20;

    cout << "Countdown will last " << count << " minutes.\n";
    for (int i = count; i > 0; --i)
        cout << i << " ";

    cout << "GO!\n";
}
If we would introduce the evil count = 13 again, it would not compile, because a constant cannot be modified. For the same reason you cannot even pass a constant to the increment() function described above.


Constant References

We have already seen how references can be used to pass an argument which is modified in the function, like this:

void decrement(int& i) {
    --i;
}

Obviously, it is legal to do something like this:

int x = 3;
decrement(x);
And obviously it is illegal to do something like this:
decrement(3);
because 3, being a number, cannot be modified. For the same reason you cannot pass a const int to decrement, like here:
const int x = 3;
decrement(3);
If you could do that -- modifying a constant -- const would be totally useless.

For the reasons explained in 8.1.1, it might be useful sometimes to pass an argument by reference and not by value even if the function does not modify it. Consider this example:

int triple(int& n) {
    return n * 3;
}
(Of course, this function is not very useful, and we could have used int instead of int& without any problems. Please bear with it as an example until I find something better.)

As one would expect, it is perfectly legal to use it as follows:

int x = 5;
cout << triple(x); // Prints '15'
But the following use, which is just as sensible, is illegal:
cout << triple(5); // ERROR

This apparently ``strange'' behavior is not strange at all. The function triple takes a reference to an int, thus it could modify it. It doesn't matter if it does or not; the fact that it could is enough for the compiler to refuse to take a constant (a literal number like 5 is constant) instead of a variable.

If you want to write a function taking a reference to something as a parameter, but you do not modify the passed object through the reference, you simply have to declare the reference as const, as follows:

int triple(const int& n) {
    return n * 3;
}
With this new function triple(5) is completely legal.

As already pointed out in 8.1.1, references are generally faster if used with user defined types; our user defined type par excellence is Wizard, and thus we'll write a function taking a const reference to a Wizard.

#include <iostream>
#include <string>

using namespace std;

struct Wizard {
    string name;
    int age;
};

void read_wizard(Wizard& wizard) {
    cout << "Name: ";
    cin >> wizard.name;
    cout << "Age: ";
    cin >> wizard.age;
}

void print_wizard(const Wizard& wizard) {
    cout << wizard.name << " is " << wizard.age << " years old.\n";
}

int main() {
    Wizard wizard;
    wizard_read(wizard);
    wizard_print(wizard);
}

It is, as already said, not possible to modify an object through a const reference; thus, something like

wizard.age = 100;
would be legal inside read_wizard but not inside print_wizard.

For the same reason, you cannot pass a const reference to a function which expects a non-const reference; in other words, you cannot call

read_wizard(wizard);
from print_wizard, but you can call
print_wizard(wizard);
from read_wizard.
next up previous contents
Next: Classes Up: Getting Started in C++ Previous: Structs   Contents
Aaron Isotton <aaron@isotton.com>
2003-02-24