In C++, whenever you pass a simple variable from one function to another, the function gets a copy of the calling value. Assigning a new value to the parameter as part of the function changes the local copy but has no effect on the calling argument. For example, if you try to implement a procedure that initializes a variable to zero using the code
void setToZero(int var) { var = 0;
}
int main() { int n, k;
cout << "Enter the number of objects (n): "; cin >> n;
cout << "Enter the number to be chosen (k): "; cin >> k;
cout << "C(n, k) = " << combinations(n, k) << endl; return 0;
}
int combinations(int n, int k) {
return fact(n) / ( fact(k) * fact(n - k) ); } n 6 k 2 720 2 24 int main() { int n, k;
cout << "Enter the number of objects (n): "; cin >> n;
cout << "Enter the number to be chosen (k): "; cin >> k;
cout << "C(n, k) = " << combinations(n, k) << endl; return 0; } n 6 k 2 15
that procedure ends up having no effect whatever. If you call setToZero(x);
the parameter var is initialized to a copy of whatever value is stored in x. The assignment statement
var = 0;
inside the function sets the local copy to 0 but leaves x unchanged in the calling program.
If you want to change the value of the calling argument—and there are often compelling reasons for doing so—you can change the parameter from the usual kind of C++ parameter (which is called a value parameter) into a reference parameter by adding an ampersand between the type and the name in the function header. Unlike value parameters, reference parameters are not copied. What happens instead is that the function receives a reference to the original variable, which means that the memory used for that variable is shared between the function and its caller. The new version of setToZero looks like this:
void setToZero(int & var) { var = 0;
}
This style of parameter passing is known as call by reference. When you use call by reference, the argument corresponding to the reference parameter must be an assignable value, such as a variable name. Although calling setToZero(x) would correctly set the integer variable x to 0, it would be illegal to call setToZero(3) because 3 is not assignable.
In C++, one of the most common uses of call by reference occurs when a function needs to return more than one value to the calling program. A single result can easily be returned as the value of the function itself. If you need to return more than one result from a function, the return value is no longer appropriate. The standard approach to solving the problem is to turn that function into a procedure and pass values back and forth through the argument list.
As an example, suppose that you are writing a program to solve the quadratic equation
ax2 + bx + c = 0
Because of your commitment to good programming style, you want to structure that program into three phases as illustrated in the following flowchart:
The Quadratic program in Figure 2-3 shows how call by reference makes it possible to decompose the quadratic equation problem in this way. Each of the functions in Figure 2-3 corresponds to one of the phases in the flowchart. The main program provides information to the function using conventional parameters. Whenever a function needs to get information back to the main program, it uses reference parameters. The solveQuadratic function uses parameters of each type. The parameters a, b, and c are used as input to the function and give it access to the three coefficients. The parameters x1 and x2 are output parameters and allow the program to pass back the two roots of the quadratic equation.
The Quadratic program also introduces a new strategy for reporting errors. Whenever the code encounters a condition that makes further progress impossible, it calls a function named error that prints a message indicating the nature of the problem and then terminates the execution of the program. The code for error looks like this:
void error(string msg) { cerr << msg << endl; exit(EXIT_FAILURE); }
The code for error uses two features of C++ that have not yet made their appearance in this book: the cerr output stream and the exit function. The cerr stream is similar to cout, but is reserved for reporting errors. The exit function terminates the execution of the main program immediately, using the value of the parameter to report the program status. The constant EXIT_FAILURE is defined in the <cstdlib> library and is used to indicate that some kind of failure occurred.
from the user. the coefficients Accept values of
Input phase:
coefficients. equation for those Solve the quadratic Computation phase:
on the screen. of the equation Display the roots
page 1 of figure. F I G U R E 2 - 3 Program to solve the quadratic equation
/*
* File: Quadratic.cpp * ---
* This program finds roots of the quadratic equation *
* 2
* a x + b x + c = 0 *
* If a is 0 or if the equation has no real roots, the * program prints an error message and exits.
*/ #include <iostream> #include <cstdlib> #include <cmath> using namespace std; /* Function prototypes */
void getCoefficients(double & a, double & b, double & c); void solveQuadratic(double a, double b, double c,
double & x1, double & x2); void printRoots(double x1, double x2);
void error(string msg); /* Main program */ int main() { double a, b, c, r1, r2; getCoefficients(a, b, c); solveQuadratic(a, b, c, r1, r2); printRoots(r1, r2); return 0; } /* * Function: getCoefficients * Usage: getCoefficients(a, b, c); * ---
* Reads in the coefficients of a quadratic equation into the * reference parameters a, b, and c.
*/
void getCoefficients(double & a, double & b, double & c) {
cout << "Enter coefficients for the quadratic equation:" << endl; cout << "a: "; cin >> a; cout << "b: "; cin >> b; cout << "c: "; cin >> c; }
The error function, of course, would be useful in many applications beyond the one that solves quadratic equations. Although you could easily copy the code into another program, it would make much more sense to save the error function in a library. In the following section, you’ll have a chance to do precisely that.
F I G U R E 2 - 3 Program to solve the quadratic equation (continued) /*
* Function: solveQuadratic
* Usage: solveQuadratic(a, b, c, x1, x2); * ---
* Solves a quadratic equation for the coefficients a, b, and c. The * roots are returned in the reference parameters x1 and x2.
*/
void solveQuadratic(double a, double b, double c, double & x1, double & x2) {
if (a == 0) error("The coefficient a must be nonzero."); double disc = b * b - 4 * a * c;
if (disc < 0) error("This equation has no real roots."); double sqrtDisc = sqrt(disc);
x1 = (-b + sqrtDisc) / (2 * a); x2 = (-b - sqrtDisc) / (2 * a); } /* * Function: printRoots * Usage: printRoots(x1, x2); * ---
* Displays x1 and x2, which are the roots of the quadratic equation. */
void printRoots(double x1, double x2) { if (x1 == x2) {
cout << "There is a double root at " << x1 << endl; } else {
cout << "The roots are " << x1 << " and " << x2 << endl; } } /* * Function: error * Usage: error(msg); * ---
* Writes the string msg to the cerr stream and then exits the program * with a standard status value indicating that a failure has occurred. */
void error(string msg) { cerr << msg << endl; exit(EXIT_FAILURE); }