6.2.2. Passing Arguments by Reference

Image

Recall that operations on a reference are actually operations on the object to which the reference refers (§ 2.3.1, p. 50):

int n = 0, i = 42;
int &r = n;           // r is bound to n (i.e., r is another name for n)
r = 42;               // n is now 42
r = i;                // n now has the same value as i
i = r;                // i has the same value as n

Reference parameters exploit this behavior. They are often used to allow a function to change the value of one or more of its arguments.

As one example, we can rewrite our reset program from the previous section to take a reference instead of a pointer:

// function that takes a reference to an int and sets the given object to zero
void reset(int &i)  // i is just another name for the object passed to reset
{
    i = 0;  // changes the value of the object to which i refers
}

As with any other reference, a reference parameter is bound directly to the object from which it is initialized. When we call this version of reset, i will be bound to whatever int object we pass. As with any reference, changes made to i are made to the object to which i refers. In this case, that object is the argument to reset.

When we call this version of reset, we pass an object directly; there is no need to pass its address:

int j = 42;
reset(j);  // j is passed by reference; the value in j is changed
cout << "j = " << j  << endl;  // prints j = 0

In this call, the parameter i is just another name for j. Any use of i inside reset is a use of j.

Using References to Avoid Copies

It can be inefficient to copy objects of large class types or large containers. Moreover, some class types (including the IO types) cannot be copied. Functions must use reference parameters to operate on objects of a type that cannot be copied.

As an example, we’ll write a function to compare the length of two strings. Because strings can be long, we’d like to avoid copying them, so we’ll make our parameters references. Because comparing two strings does not involve changing the strings, we’ll make the parameters references to const2.4.1, p. 61):

// compare the length of two strings
bool isShorter(const string &s1, const string &s2)
{
    return s1.size() < s2.size();
}

As we’ll see in § 6.2.3 (p. 213), functions should use references to const for reference parameters they do not need to change.


Image Best Practices

Reference parameters that are not changed inside a function should be references to const.


Using Reference Parameters to Return Additional Information

A function can return only a single value. However, sometimes a function has more than one value to return. Reference parameters let us effectively return multiple results. As an example, we’ll define a function named find_char that will return the position of the first occurrence of a given character in a string. We’d also like the function to return a count of how many times that character occurs.

How can we define a function that returns a position and an occurrence count? We could define a new type that contains the position and the count. An easier solution is to pass an additional reference argument to hold the occurrence count:

// returns the index of the first occurrence of c in s
// the reference parameter occurs counts how often c occurs
string::size_type find_char(const string &s, char c,
                           string::size_type &occurs)
{
    auto ret = s.size();   // position of the first occurrence, if any
    occurs = 0;            // set the occurrence count parameter
    for (decltype(ret) i = 0; i != s.size(); ++i) {
        if (s[i] == c) {
            if (ret == s.size())
                ret = i;   // remember the first occurrence of c
            ++occurs;      // increment the occurrence count
         }
    }
    return ret;            // count is returned implicitly in occurs
}

When we call find_char, we have to pass three arguments: a string in which to look, the character to look for, and a size_type3.2.2, p. 88) object to hold the occurrence count. Assuming s is a string, and ctr is a size_type object, we can call find_char as follows:

auto index = find_char(s, 'o', ctr);

After the call, the value of ctr will be the number of times o occurs, and index will refer to the first occurrence if there is one. Otherwise, index will be equal to s.size() and ctr will be zero.


Exercises Section 6.2.2

Exercise 6.11: Write and test your own version of reset that takes a reference.

Exercise 6.12: Rewrite the program from exercise 6.10 in § 6.2.1 (p. 210) to use references instead of pointers to swap the value of two ints. Which version do you think would be easier to use and why?

Exercise 6.13: Assuming T is the name of a type, explain the difference between a function declared as void f(T) and void f(T&).

Exercise 6.14: Give an example of when a parameter should be a reference type. Give an example of when a parameter should not be a reference.

Exercise 6.15: Explain the rationale for the type of each of find_char’s parameters In particular, why is s a reference to const but occurs is a plain reference? Why are these parameters references, but the char parameter c is not? What would happen if we made s a plain reference? What if we made occurs a reference to const?


..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.145.50.222