Pass By Value vs. By Reference
There are two different ways to pass arguments to a function, each with its own tradeoffs.
Pass by value
When you pass a variable to a function, C++ copies its value into a parameter. The function works on that copy. Whatever the function does to that copy, the original stays untouched.
- On line 8,
originalPriceis passed as an argument, and its value is copied into the parameterprice. - On line 4,
priceis reassigned a different value before it is returned. - On line 10, the value returned from the function is assigned to a new variable,
finalPrice.
Now notice, in the output, how originalPrice remains unchanged after the function call:
Original: 49.99
Final: 39.992This is called passing by value. Every parameter in C++ works this way by default.
Pass by reference
But sometimes changing the original is the entire point. For example, consider a function that swaps the values in two variables:
The logic inside swap is correct. a and b really do swap values. But a and b are just copies, so swapping them does not swap x and y, which was the actual intent of calling swap(x, y).
To give swap direct access to x and y, you need what’s called a reference parameter. A reference parameter refers to the caller’s variable directly, instead of holding a copy of it.
You define it by writing an ampersand & after the type in the function’s parameter list. Line 3 shows how:
Now a inside swap isn’t a copy of x, it’s another name for x itself. If you change a you’ll see the same change in x. The same holds for b and y. This is called passing by reference.
Use case 1: when returning one value isn’t enough
A function can only return one thing. Reference parameters are one way to get multiple results out of a function.
Here, smallest and largest can be thought of as outputs of the function. The caller passes in uninitialized variables, and the function fills them in using references.
Use case 2: when copying is wasteful
References aren’t only for when you want to modify something. They’re also how you avoid copying large data when you only need to read it.
On line 4, name is declared as a reference but with a const keyword before it. This combination, const std::string&, does two things at once:
&avoids the copying cost.constguarantees that the originalusernamecannot be modified through this reference. Anyone who calls this function in the future can rest assured it won’t inadvertently modify their variable.
Small types like int or double are cheap to copy, cheaper than the cost of setting up a reference, so passing them by value is fine and normal. The const & pattern makes more sense for things like std::string, or other types that might hold a lot of data.
Checkpoint
What does this print?
void applyDiscount(double amount) {
amount = amount * 0.9;
}
int main() {
double price = 200;
applyDiscount(price);
std::cout << price;
}The function call adjustSpeed(60, 10); results in a compiler error. Why?
void adjustSpeed(double& speed, double delta) {
speed = speed + delta;
}