Previous chapter: Chapter 7: Pointers and Memory Management in C++
Building on the concept of memory addresses, this chapter explores references and the critical distinction in C++ functions between passing data by value (copy) and passing data by reference (alias).
1. References: Aliases for Variables
A reference is a second name, or an alias, for an existing variable. Once initialized, a reference acts exactly like the variable it refers to. Unlike pointers, a reference cannot be changed to refer to a different variable later, and it cannot be null.
Declaring a Reference
You declare a reference by placing the address-of operator (&) after the data type.
int original_score = 95;
int& alias_score = original_score; // alias_score is a reference to original_score
// Changing the reference changes the original variable
alias_score = 100;
std::cout << "Original score: " << original_score << std::endl; // Output: 100
In many ways, a reference provides the power of a pointer (working directly with the original memory) without the complex syntax of the dereference operator (*).
2. Parameter Passing: Call by Value
When an argument is passed to a function using call by value, a separate, temporary copy of the argument's data is created within the function's scope.
Effect: Any changes made to the parameter inside the function are applied only to the copy and do not affect the original variable outside the function.
void increment_value(int num) {
num++; // Only the copy is incremented
}
int main() {
int x = 5;
increment_value(x);
// x remains 5 outside the function
return 0;
}
3. Parameter Passing: Call by Reference
To allow a function to modify the original variable, you must use call by reference. This can be achieved in two ways:
A. Reference Parameters (The C++ Way)
This is the cleanest and safest way. You pass the function a reference to the original variable.
void increment_reference(int& num) {
num++; // The original variable is incremented
}
int main() {
int x = 5;
increment_reference(x);
// x is now 6
return 0;
}
B. Pointer Parameters (The C Way)
You pass the function a pointer (the memory address) to the original variable, and then dereference the pointer inside the function to change the value.
void increment_pointer(int* num_ptr) {
(*num_ptr)++; // Dereference and increment the original value
}
4. const References: Efficiency and Safety
When passing large objects (like a custom class or a large std::string) to a function, copying the object (call by value) is inefficient. However, if the function doesn't need to change the object, you can pass it as a const reference.
// Passes the address (efficient) but prevents modification (safe)
void print_string(const std::string& text) {
std::cout << text << std::endl;
// text += " cannot modify!"; // ERROR: const prevents changes
}
This pattern is widely used in C++ for performance gains without sacrificing data integrity.