Parameter passing¶
When creating a variable, a C++ programmer has the option of deciding whether they will create a genuine variable or only a reference to an existing variable. Because of this, they have new possibilities also with formal parameters of a function.
Before giving a definition for formal parameters, we need to know what is meant by actual parameters. They parameters that are used in function call. Formal parameters, instead, are used in function definition. Therefore, they are variables that help access the actual parameters or their values in function body.
Unless the programmer decides otherwise, the function parameters of C++ are value parameters by default.
#include <iostream>
using namespace std;
void value_parameter_function(int number) {
number = 2 * number;
}
int main() {
int variable = 7;
cout << variable << endl; // Prints 7
value_parameter_function(variable);
cout << variable << endl; // Prints again 7
}
In the code above, the formal parameter called number
is a new variable.
It is initialized to the value of the actual parameter variable
,
which is 7.
Although the function above will double the value of its formal parameter,
it does not have an effect on the actual parameter, since this is a
separate variable.
If desired, a programmer can define some or all of the formal parameters of a function as reference parameters.
#include <iostream>
using namespace std;
// The only differences in this new version are
// - the name of the function has been changed for clarity's sake
// - ampersand character (&) has been added in the parameter definition.
void reference_parameter_function(int& number) {
number = 2 * number;
}
int main() {
int variable = 7;
cout << variable << endl; // Prints 7
reference_parameter_function(variable);
cout << variable << endl; // Prints 14
}
In the new version, the parameter number
is a reference
parameter, and the changes made to it are immediately reflected in the actual
parameter variable
.
Because of the fact that in C++, a reference gives an additional name to an existing variable (or, more precisely, to a memory area that is already in use), the following code snippet is not correct:
void reference_parameter_function(int& number) {
number = 2 * number;
}
int main() {
reference_parameter_function(7); // ERROR
}
The reason: a literal cannot have references in C++, because it is not a variable.
Without a closer look, one might think that Python’s function parameters are value parameters. In reality, all of the ”variables” in Python follow reference semantics (which means they are references), including the functions’ parameters.
The mistaken conclusion might, in part, be based on the fact that a Python programmer must also remember the concepts of a mutable and immutable data type, which are Python’s mechanism to produce reference semantics for some of its data types. A value of the immutable data type as a function’s parameter will seemingly act as if it was a value parameter.
Constant parameters¶
We had a situation where we wanted to make changes inside a function
to the variable passed by as a parameter.
Some situations can be the opposite:
Within a function, you want to leave the variable passed by as a parameter
unchanged (or make it unchangeable).
In this case you can define the parameter as constant with the
keyword const
.
(We used this keyword already in section 2.3 to define the named constants.)
The keyword const
will be written before the formal parameter,
as demonstrated below:
void constant_parameter_function(string const& word) {
cout << word << endl;
string local_word = word;
}
The use of parameters defined as constant is restricted, because the C++ compiler makes sure that the parameter will not be changed. For example, the code snippet you see above prints the parameter and its value is assigned to the local variable, and both of these actions are legal. Below you can see an example of illegal use of a constant parameter:
void constant_parameter_function(string const& word) {
word = "new word"; // ERROR
}
int main() {
constant_parameter_function("moi"); // CORRECT
}
In the examples above the parameter word
is a reference parameter.
What is the point of it? Just earlier the reference parameters made it
possible to change the formal parameter such that the change also
affects the actual parameter?
When we defined the parameter as constant, wasn’t it supposed to prevent
this from happening?
Let us first see if the constant parameter should be a value parameter. It is entirely possible, but the only extra benefit would be the compiler reporting an error if we tried to change the value of the parameter. It would not change the situation much, because as we noticed above, changing the value of the formal parameter does not affect the actual parameter.
In that case, what is the point of using a reference parameter as the constant parameter? On this course we have already discussed value semantics and reference semantics. When you assign a value using value semantics, you copy the value, but in reference semantics you simply change the target of the reference to be a different data element. The difference between value and reference parameters is similar. Let us imagine that the parameter is a very large data structure or container (which we will discuss further in the next two rounds). Copying the whole data structure, element by element, is a hard task if you compare it to just taking the reference that targets the beginning of the said data structure, and moving it to take a new target.
Therefore if we want to pass a large data structure by as a parameter, the most reasonable way to do this is to pass it as a reference. If we also wish not to change the parameter in question (even by accident) within the function, the best choice is to define the parameter as constant.
For the reasons mentioned above, the constant parameters are often references. (They can also be pointers, which we will study later as the course advances.)
Note that primitive types (such as int
) are not reasonable to
pass as constant references, but it is better to pass them as values.
Namely, constant references would not have any efficiency benefits,
since the size of a primitive type is (at least nearly) the same
as that of a reference.