Section 1 - Reference Parameters

7B.1.1 Reference, Const and Passing an Object to a Method

We've already seen that when we pass primitive data as arguments to methods, that data cannot be modified by the method in such a way that it changes after the method call is complete.  Whatever value it had before, it still has.  Here is an example:

#include <iostream>
using namespace std;

// method prototypes
void promisePieceOfCarrot( int freq );

int main()
{
long chloesWagFrequency = 30;

cout << "Chloe's wag frequency is "
<< chloesWagFrequency << " wags per minute.\n";
promisePieceOfCarrot( chloesWagFrequency );
cout <<"Chloe's wag frequency is "
<< chloesWagFrequency << " wags per minute.\n";
}

// a would-be modifying method
void promisePieceOfCarrot( int freq)
{
freq *= 3;
}

Here's the run:

Chloe's wag frequency is 30 wags per minute.
Chloe's wag frequency is 30 wags per minute.

No surprise there.

7B.1.2 Passing Objects to Methods

This time, lets wrap the integer into a Dog class and pass a Dog object as an experiment.  In Java and VB this would work, so maybe it does in C++, too.

Caution

In this example, I am going to declare everything public and allow the client access to the data.  This is only to keep the code short. Normally, in a real program or assignment, we would not think of making member data public.

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

// class prototypes
class Dog
{
public:
string name;
int wagsPerMinute;
};

// method prototypes
void promisePieceOfCarrot(Dog dog);

int main()
{
Dog chloe;

// public access not usually good, but do it anyway
chloe.name = "Chloe of Hood Canal";
chloe.wagsPerMinute = 30;

cout << chloe.name << "'s wag frequency is "
<< chloe.wagsPerMinute << endl;
promisePieceOfCarrot( chloe );
cout << chloe.name << "'s wag frequency is "
<< chloe.wagsPerMinute << endl;
return 0;
}

// a would-be modifying method
void promisePieceOfCarrot(Dog dog)
{
dog.wagsPerMinute *= 3;
}

Here is the run:

Chloe of Hood Canal's wag frequency is 30
Chloe of Hood Canal's wag frequency is 30

No improvement.  Objects, like primitive data, are passed by value.  No change is sustained.  C++ obviously needs a different technique for modifying the data in its formal parameter list.  Fortunately, there is one.

7B.1.3 Reference Parameters

If, in the formal parameter list of a method, we use the ampersand, &, in front of a parameter, we are calling this parameter a reference parameter.  Technically it means we are passing an object reference rather than the object itself, to the method.  But all we need to know is that it gives us what we want:  a way to change the values of the parameters. 

Change the function header (both in the prototype and the definition) so that it has an & in it like so:

// method prototypes
void promisePieceOfCarrot(Dog &dog);

// a would-be modifying method
void promisePieceOfCarrot(Dog &dog)
{
dog.wagsPerMinute *= 3;
}

Now look at the output:

console shot

Voila!  This works for primitive types such as int and double as well as class object parameters. (Try it on our first program that used just ints).  However, reference parameters have a magnified benefit when applied to objects vs. primitive data.   Imagine that we wanted a method to modify seven different ints in our client, main().  We could pass all seven ints as reference parameters to the method, but that would be ugly:  some_func(param1, param2, param3, ... , param7);  Or we could wrap the seven ints into a class definition and pass one single object of that class to the method, some_func(the_object);.  Much neater.  We're going to do this a little later.

Summary: When passing a primitive or object to a method, its value cannot be modified upon return to the client.  When passing a reference to a method, the data can be modified permanently by the method.

Just remember that Chloe's wagging tail goes faster after she sees the carrot.

7B.1.4 Preventing Unintended Side-Effects: Const Parameters

Now that we know how to modify a parameter in a way that persists in the caller, we have a new problem.  (The moment you fix one thing, something else seems to break). 

Programmers use the reference mechanism for reasons other than modifying a parameter passed into the function.  When we pass large objects into a function by value (i.e., without the &) it moves a lot of data down into it and this takes time and space.  By passing a reference to the object rather than the object itself, a much lighter entity is passed down - the address of the object.  This is wonderful for speed and efficiency.  So the reference parameter has a second application, namely, that of easing the burden of the processor when executing functions that take objects as parameters.  Since the programmer does not intend to modify the object, it would be nice to make sure that he can't;  it would be nice if the compiler issued an error if we inadvertently tried to make a change to the object that was passed by reference.  This is done by placing const in front of the reference parameter.

In the last program, we were able to allow Chloe's tail to wag faster as a result of promisePieceOfCarrot(Dog &dog) because of the reference Dog&.  If we did not want to allow the change to happen, yet we still wanted to pass the object by reference, we would do it like so:

// method prototypes
void promisePieceOfCarrot(const Dog &dog);

// a would-be modifying method
void promisePieceOfCarrot(const Dog &dog)
{
// do a bunch of things that involve "promising" the dog a carrot
...

// but later, we accidentally do this:
dog.wagsPerMinute *= 3;
}

Now the method will not compile.  We'll get an error on the line where we try to change the dog's wagsPerMinute.  And just to be clear, we want the error.  That's the point.  We are putting protectionist code into the function header in order to force us to behave ourselves when we are writing the function.

The moral is this.  If you don't intend to change a parameter's values inside a function, yet you want to pass it by reference, make it a const parameter.

7B.1.5 Instance Methods That Take Objects as Parameters

We showed a global-scope (i.e., non-member) method that takes Dog objects as parameters (see above) .   Can we pass an object to an instance method of a class?  Sure, if it makes sense to do so.  If we have a class Dog, we normally would not design an instance method in that class that takes a single Dog object as a parameter, because there is automatically one object you get for free with every instance method invocation, namely the "this" object, inherent in every instance call.   So it usually doesn't make sense to pass an object of the same class to an instance method of that class for this very reason. You don't need to.  You get one object for free without passing any parameters!

But sometimes we have a function that wants a second object, and if so, we may pass that second object as a parameter.  For example, maybe we want to see the difference in wag rates between a one dog and another.  We could write this as a global scope function that takes two Dogs, but here's a another idea: we can make it an instance method of the Dog class, with one Dog being the object doing the calling (this) and the other Dog being the parameter.

From the client point of view the call would look like this (perhaps):

cout << "The difference in wag frequency between "
<< chloe.name << " and " << lily.name
<< " is " << chloe.differenceInWags( lily )
<< endl;

Let's see how this works in the instance method definition:

int Dog::differenceInWags( Dog otherDog )
{
return wagsPerMinute - otherDog.wagsPerMinute;
}

As you see, it is straightforward -- the this object's member is accessed, as we already learned, without any dereference inside the method;  wagsPerMinute, stark naked in the method definition, can mean only one thing:  the this object's wagsPerMinute.  The parameter's wagsPerMinute, on the other hand, is distinguished from that because we use the formal parameter, otherDog,  to dereference it:  otherDog.wagsPerMinute.

Observation

Even if wagsPerMinute had been private, we could still have accessed both the this object's member and the parameter's member without using accessor methods.  We would just refer to the members as we did in the above method.  The private data of the class is available to any class method, even if we are accessing some non-this object's member, as we did above with the parameter otherDog.

Here is the full program:

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

// class prototypes
class Dog
{
public:
string name;
int wagsPerMinute;
int differenceInWags( Dog otherDog );
};

int main()
{
Dog chloe, lily;

// public access not usually good, but do it anyway
chloe.name = "Chloe of Hood Canal";
chloe.wagsPerMinute = 30;
lily.name = "Lily the Waggiest";
lily.wagsPerMinute = 75;

cout << "The difference in wag frequency between "
<< chloe.name << " and " << lily.name
<< " is " << chloe.differenceInWags( lily )
<< endl;

return 0;
}

int Dog::differenceInWags( Dog otherDog )
{
return wagsPerMinute - otherDog.wagsPerMinute;
}

/* ---------------- Paste of Run --------------------

The difference in wag frequency between Chloe of Hood Canal and Lily the Waggiest
is -45
Press any key to continue . . .

----------------------------------------------- */