Class 18: Functions III

Section 5.2 of Problem Solving with C++

Lecture

Pass by Reference rather than Value

Recall that arguments to our functions have been passed by value, meaning that inside the function we get a copy of the argument object given in the function call. Sometimes, however, we'd like to get the actual object from the function call rather than a copy. There are 3 basic reasons for this:

1.      We may want to modify the object.

2.      A "copy" may not make sense for some objects.

3.      If an object is "large", copying may be expensive in terms of time or memory space.

As an example of the first reason: Suppose we have a variable that stands for the hour (with a 24 hour clock), and we go through a simulation that keeps incrementing the hour. A simple ``` ++``` won't do, because once we hit 25, the hour goes back to 1. So, we might want a function ``` increaseHour``` that increments the hour correctly ... which means that the hour variable with which the function gets called should be modified. We might be tempted to say something like this:

`void increaseHour(int);`
`...`
`void increaseHour(int hourIn)`
`{`
`  hourIn++;`
`  if (hourIn == 25)`
`    hourIn = 1;`
`}`
`    `

However, since pass-by-value gives us a copy, nothing really gets changed! We need to pass the hour by reference, which means we get the actual object from the function call, not a copy. This is indicated in C++ by putting an `&` after the type of the argument.

`void increaseHour(int&);`
`...`
`void increaseHour(int& hourIn)`
`{`
`  hourIn++;`
`  if (hourIn == 25)`
`    hourIn = 1;`
`}`
`    `

Now this does what we want. For example, if this function definition is used in the following code fragment:

`int hour = 18;`
`for(int i = 0; i < 8; i++)`
`{`
`  cout << "Hour " << i + 1 `
`       << " of my 8 hour day starts at "`
`       << hour << ":00" << endl;`
`  increaseHour(hour);`
`}`

This fragment will print out something like this:

 `Hour 1 of my 8 hour day starts at 18:00``Hour 2 of my 8 hour day starts at 19:00``Hour 3 of my 8 hour day starts at 20:00``Hour 4 of my 8 hour day starts at 21:00``Hour 5 of my 8 hour day starts at 22:00``Hour 6 of my 8 hour day starts at 23:00``Hour 7 of my 8 hour day starts at 24:00``Hour 8 of my 8 hour day starts at 1:00`

Note that ``` increaseHour(6)``` will not compile! Why? Well when you pass something like this by reference, that says that it might get modified, and the constant value 6 is not something that can be modified! You need to pass variables, not constants, to functions that take arguments by reference rather than by value.

Power Point Animation of Pass by Reference versus Pass by Value

The World Famous ``` swap```

With multi-parameter functions, we really start to see some interesting reasons to use pass-by-reference. For example, one of the most common operations in computing is the `swap`. Remember when we wanted to start off the GCD algorithm with ``` int```s `a` and `b`, we wanted to be sure that `a` was the larger of the two. If we had a function `swap` that took two `int`s and swapped their values, we could do the following:

`if (b > a)`
`  swap(a,b);`
` `
`while (b != 0)`
`{`
`  r = a % b;`
`  a = b;`
`  b = r;`
`}`
`cout << a << " is the GCD!" << endl;`

The function ``` swap``` will need to change the values of the variables it's passed, so they must be passed by reference.

`void swap(int&, int&);`
`...`
`void swap(int& leftIn, int& rightIn)`
`{`
`  int temp = leftIn;`
`  leftIn = rightIn;`
`  rightIn = temp;`
`}`
` `

Using reference to return multiple values

Sometimes there are several things we'd like to return with a function. For example, if you have a program that works with vectors, you might want to convert back and forth from the (r,theta) and (x,y) representation. It'd be nice to have a function that would do this, so we'd give it (r,theta) and it'd give us the appropriate (x,y). Unfortunately, there are two values we'd need to return from our function, and a function always returns at most one thing. What we can do, is pass a variable for x and a variable for y by reference, and modify those variables inside the function so that they contain the proper values.

`void polar2rect(double,double,double&,double&);`
`...`
`void polar2rect(double r, double theta, double &x, double &y)`
`{`
`  x = r*cos(theta);`
`  y = r*sin(theta);`
`}`
`    `

With this definition, if I had a vector represented by `double`s `radius` and ``` angle```, I could convert it to rectangular coordinates stored in variables `x` and ``` y``` by writing:

`polar2rect(radius, angle, x, y);`

Here is an example of a function that returns a projectile height, range, and flight time in a single function call (projectile).

Using reference when copying an object doesn't make sense

Reason 2 from my list of reasons to pass by value is that copying doesn't make sense for some type of objects. Consider the type ``` istream```, recalling that both ``` cin``` and all the ``` ifstream``` objects we declare are of type ``` istream```.

Suppose I have an integer variable ``` T``` whose value I'd like to be the number of seconds elapsed between two events, and I'd like to read that in our ``` hh:mm:ss``` format. This is actually a fair bit of work ... not difficult, but time consuming. Let's further suppose that I'll be reading this from both the keyboard (using ``` cin```) and from a file (using an ``` ifstream``` object named ``` filein```) at various points in my program. It would be wonderful to have a function `readtime` that could take either `cin` or `filein` as an argument, read the elapsed time in `hh:mm:ss` format, and return it in seconds. Since both are ``` istream``` objects, it would be natural to write the function

`int readtime(istream);`

The problem with this, however, is that it doesn't make sense to copy an input stream object. What would it mean? What would happen, for example, if there was an error while reading the copy ... would the original still be OK? That wouldn't be good. On the other hand, we just have a copy, so it can't affect the original. We have a problem! Copying doesn't make sense. So, we must pass stream objects (both ``` ostream``` and ``` istream```) by reference. With this in mind, we'd define:

`int readtime(istream&);`
`...`
`int readtime(istream& in)`
`{`
`  int h, m, s;`
`  char c;`
`  in >> h >> c >> m >> c >> s;`
`  return h*3600 + m*60 + s;`
`}`

With these definitions, I can say

`int k = readtime(cin);`

... when I need to read in a time from the keyboard, and I can say:

`int m = readtime(filein);`

... when I need to read in a time from the file to which `filein` is attached. Now, my list also mentioned efficiency as a reason to pass by reference rather than pass by value. We haven't seen any explicit examples of "large" objects for which this is important. Our usual types - int, double, char - are all small, so there is no advantage (actually there's a small disadvantage!) to using pass by reference instead of pass by value.

Special Note: Why pass filestreams?

Why passing filenames to functions usually is a bad idea

Suppose I want to write a program that reads in two lengths from a file, given in x' y" format, and print out the difference in inches. It should work something like this:

 Program Run data.txt `Enter file name: data.txt``3" difference` `3' 2"``2' 11"`

As I ponder how to do this, I realize code that reads a length and converts it to inches consists of a couple of lines, and those lines are the same whether I'm reading the first length or the second. So, in top-down design fashion I say to myself "It sure would be nice to have a function `readLength` that would take care of the reading and converting for me." And so we might arrive at the following proposed solution.

 Prototypes Main Definitions `int readLength(string fname);` `int main()` `{` `  string DFname;` `  cout << "Enter file name: ";` `  cin >> DFname;` `  int L1 = readLength(name);` `  int L2 = readLength(name);` `  cout << L1 - L2 << "\" difference"` `       << endl;` `  return 0;` `}` `int readLength(string fname)` `{` `  ifstream fin(fname.c_str());` `  int f, i;` `  char c;` `  fin >> f >> c >> i >> c;` `  return 12*f + i;` `}`

When I run this on the file ``` data.txt``` from above, the result is ``` 0" difference```, which is not what I want. What happened? It is well worth running through this with the debugger and/or with your professor. What you should see is that the first call to ``` readLength``` creates the variable ``` fin```, opens ``` data.txt``` and attaches it to ``` fin```, and reads in the first line, i.e. `3' 2"`. When the call is over, the stream is closed and `fin`, being a local variable, is destroyed. then we go through the second call to ``` readLength``` and ... the exact same thing happens. In other words, `readLength` creates the variable `fin`, opens `data.txt` and attaches it to `fin`, and reads in the first line, i.e. `3' 2"`. So both calls read in the first line of the file, the second line is never read, and in `main` `L1` and ``` L2``` both have the same value!

The moral of the story is this: passing a file name to a function only makes sense if you want that one function call to completely process the file. If each function call is only going to process a piece of the file, and if a later call should pick up in the file where the earlier call left off, you can't pass the file's name. Instead, you need to pass the file stream to the function.

Passing file streams to functions

So, to get the above scheme to work, `main` should be the one that creates an `ifstream` object and attaches it to `data.txt`, and that `ifstream` object is what should then be passed to `readLength` in the two function calls.

 Prototypes Main Definitions `int readLength(ifstream &fin);` `int main()``{``  string name;``  cout << "Enter file name: ";``  cin >> DFname;``  ifstream DFin(name.c_str());``  int L1 = readLength(DFin);``  int L2 = readLength(DFin);``  cout << L1 - L2 << "\" difference"``       << endl;``  return 0;``}` `int readLength(ifstream &fin)``{``  int f, i;``  char c;``  fin >> f >> c >> i >> c;``  return 12*f + i;``}`

Now we have a working program, one that prints out `3"` when we run it on the file `data.txt` from above, just like it's supposed to. Notice that ``` readLength``` takes the parameter ``` fin``` by reference. Why? Well first of all, the whole point is that we have a single filestream and both function calls read from it. If we have multiple filestreams because a pass-by-value call made a copy, we'd be back in the same situation as before: both calls would read the first line of the file. Moreover, making copies of input and output streams simply doesn't make sense: many compilers won't even let you do it. So, input and output streams are always passed by reference!

Why giving your parameters type ``` istream``` is more flexible than ``` ifstream```

Now we have a program that works, and we have this `readLength` function that is potentially useful in other programs as well. Can we make ``` readLength``` even more useful than it already is? Well, recall that we discussed that ``` cin``` has type `istream`, and that `ifstream` objects like `DFin` in the previous program are also of type `istream` (as well as being of type `ifstream`). This (and the analogous output situation) is our one and only example of what are called subtypes. How can something be of type ``` istream``` and type ``` ifstream``` at the same time? Well, you manage to be of type "college student" and "midshipman" at the same type. It's possible because "midshipman" is a specific kind of "college student". In other words, "midshipman" is a subtype of "college student". The readLength function doesn't use anything specific to `ifstream` that other `istream` objects don't also have or do. Therefore, we could simply rewrite ``` readLength``` so that it takes an ``` istream``` object.

 Prototypes Main Definitions `int readLength(istream &in);` `int main()``{``  string name;``  cout << "Enter file name: ";``  cin >> DFname;``  ifstream DFin(name.c_str());``  int L1 = readLength(DFin);``  int L2 = readLength(DFin);``  cout << L1 - L2 << "\" difference"``       << endl;``  return 0;``}` `int readLength(istream &in)``{``  int f, i;``  char c;``  in >> f >> c >> i >> c;``  return 12*f + i;``}`

Everything works just like before. So what's the advantage? Well, since `cin` is an `istream` object (though not an `ifstream` object), we can read a length from the keyboard with ``` readLength``` by the function call ``` readLength(cin)```. So now we have a ``` readLength``` that's even more powerful because it can read from files and from the keyboard.

What's the moral of this story? If you have a function that reads (or writes) information, and if the function doesn't use any `ifstream` (or ``` ofstream```) specific things like ``` .open``` or ``` .close```, define your function to take an argument of type `istream` (or ``` ostream```). This way it can read (or write) to files and to the screen. BTW: remember to pass by reference!

Composing Functions

Let's suppose that I had the function `max` defined as

`int max(int a, int b)`
`{`
`  if (b > a)`
`    return b;`
`  else`
`    return a;`
`}`
```but that my program had three `int`s, `x`, `y`, and `z`, amongst which I need the largest. If I wrote max(x,y,z) the compiler would complain ... the only `max` function it knows about only
takes two arguments! However, I could say the following: max(max(x,y),z).```

This is our first example of composition of functions. When the function `max()` gets called, its two argument expressions are evaluated. The first is ``` max(x,y)```, which evaluates to the larger of the two values, and the second is simply `z`. So, what we get out of this is the maximum of all three values.

Problems

1.      Incrementing a Military Clock (You might prefer this solution.) This is a simple pass-by-reference example that modifies its argument.

2.      Reading Binary Numbers Here's a simple example in which we use pass-by-reference to avoid making a copy of an istream object.

Assoc Prof Christopher Brown