++
won't do, because once we hit 25, the hour goes back to 1. So,
we might want a function incHour
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 incHour(int);
// ...
void incHour(int h)
{
h++;
if (h == 25)
h = 1;
}
However, since pass-by-value gives us a copy, nothing really gets
changed! (See diagram above.)
&
after the
type of the argument.
void incHour(int&);
// ...
void incHour(int& h)
{
h++;
if (h == 25)
h = 1;
}
The visualization here is that the parameter h
is like a wormhole to the argument incHour
was called
with. (See diagram above.)
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 = 1; i <= 8; i++)
{
cout << "Hour " << i << " of my 8 hour day starts at " << hour << ":00" << endl;
incHour(hour);
}
In this case, you should write incHour() as a function that takes its parameter with pass-by-value and returns the new time, rather than modifying its argument.
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
incHour(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.
source code | compiler error |
|
ex.cpp: In function ‘int main()’: ex.cpp:8:12: error: invalid initialization of non-const reference of type ‘int&’ from an rvalue of type ‘int’ incHour(6); ^ ex.cpp:4:6: note: initializing argument 1 of ‘void incHour(int&)’ void incHour(int& h); ^ |
Q: What can be an lvalue then?
Answer: variables or objects in an array
We require lvalues for arguments to functions in which the parameter is passed
by reference.
Recall that lvalue is something that can appear on the left-hand side (i.e.,
whose value can be changed by an assignment). So, just see if whether an
assignment makes sense when you put it on the left-hand side.
For example, consider the following code:
|
|
|
|
swap
!swap
.
For example, suppose we read two int's in from the user and we want to print out all the integer values from the smaller of the two up to the larger (comma-separate). If the user is kind enough to enter the numbers so that the frst is the smaller, we would write:
If we had
a function swap
that took two int
s and
swapped their values, we could do the following:
int a, b;
cin >> a >> b;
if (b < a)
swap(a,b); // the famous swap!
for(int i = a; i < b; i++)
cout << i << ',';
cout << b << endl;
... which is a whole lot more convenient than, for example,
writing separate for-loops for the a<b case and the b<a case.
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& a, int& b)
{
int temp = a; // We need a temporary variable. Why?
a = b;
b = temp;
}
For example, you may want to have a function readfile
that works as follows:
N = 4 3.1 4.1 5.1 6.1
double* readfile(int& N); // Note the reference type for N.
In the main function, you can call the function like:
int N;
double* A = readfile(N);
cout << "data: " << endl;
for(int i=0; i < N; i++)
cout << A[i] << " ";
Q: Define readfile
.
double* readfile(int& N)
{
ifstream fin("data.txt");
char c;
fin >> c >> c;
fin >> N; // Due to pass-by-reference, N in main() will change too
double* A = new double[N];
for(int i=0; i < N; i++)
fin >> A[i];
return A;
}
#include <fstream>
using namespace std;
int main()
{
ofstream f1("data.txt");
ofstream f2;
f2 = f1; // not allowed
return 0;
}
Q: Compile the code. Does this code compile successfully?
No, I see a massive amount of error messages.
You don't need to understand the detail of the messages, but you show know that
you cannot do f1=f2
.
void foo(ifstream );
int main()
{
ifstream fin("data.txt");
foo(fin); // pass by value doesn't work
//...
}
ostream
and istream
) by reference.
With this in mind, we'd define a function:
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;
}
As you see, the function reads the elapsed amount of time in our
hh:mm:ss
format.
With these definitions, I can say
int k = readtime(cin);
when I need to read in a time from the keyboard.
cin
and all the ifstream
objects
can be passed as a reference to istream
.
As you know, ifstream
almost works the same as
istream
. In fact, ifstream
a is sub-type of
istream
. (There are other sub-types of istream
e.g.,
istringstream
).
Therefore, I can also say:
ifstream fin("data.txt");
int m = readtime(fin); // pass fin as a reference to istream
... when I need to read in a time from a file data.txt
using the same function readtime
.
It would be wonderful to have a function readtime
that can take
either cin
or fin
as an argument, read the elapsed
time in hh:mm:ss
format, and return it in seconds.